fal 1.26.3__tar.gz → 1.26.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fal might be problematic. Click here for more details.
- {fal-1.26.3/fal.egg-info → fal-1.26.5}/PKG-INFO +2 -2
- {fal-1.26.3 → fal-1.26.5/fal.egg-info}/PKG-INFO +2 -2
- {fal-1.26.3 → fal-1.26.5}/fal.egg-info/SOURCES.txt +1 -0
- {fal-1.26.3 → fal-1.26.5}/fal.egg-info/requires.txt +1 -1
- {fal-1.26.3 → fal-1.26.5}/pyproject.toml +1 -1
- {fal-1.26.3 → fal-1.26.5}/src/fal/_fal_version.py +2 -2
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/apps.py +4 -1
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/profile.py +65 -8
- {fal-1.26.3 → fal-1.26.5}/src/fal/config.py +21 -17
- {fal-1.26.3 → fal-1.26.5}/src/fal/files.py +33 -10
- {fal-1.26.3 → fal-1.26.5}/src/fal/sdk.py +3 -2
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/file/providers/fal.py +21 -32
- fal-1.26.5/tests/test_files.py +59 -0
- {fal-1.26.3 → fal-1.26.5}/tests/test_stability.py +40 -1
- {fal-1.26.3 → fal-1.26.5}/.gitignore +0 -0
- {fal-1.26.3 → fal-1.26.5}/Makefile +0 -0
- {fal-1.26.3 → fal-1.26.5}/README.md +0 -0
- {fal-1.26.3 → fal-1.26.5}/docs/conf.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/docs/index.rst +0 -0
- {fal-1.26.3 → fal-1.26.5}/fal.egg-info/dependency_links.txt +0 -0
- {fal-1.26.3 → fal-1.26.5}/fal.egg-info/entry_points.txt +0 -0
- {fal-1.26.3 → fal-1.26.5}/fal.egg-info/top_level.txt +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/README.md +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi-fal-rest/pyproject.toml +0 -0
- {fal-1.26.3 → fal-1.26.5}/openapi_rest.config.yaml +0 -0
- {fal-1.26.3 → fal-1.26.5}/setup.cfg +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/__main__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/_serialization.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/_version.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/api.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/app.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/apps.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/auth/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/auth/auth0.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/auth/local.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/_utils.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/api.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/auth.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/cli_nested_json.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/create.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/debug.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/deploy.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/doctor.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/files.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/keys.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/main.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/parser.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/run.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/runners.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/secrets.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/cli/teams.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/console/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/console/icons.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/console/ux.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/container.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/exceptions/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/exceptions/_base.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/exceptions/_cuda.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/exceptions/auth.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/flags.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/logging/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/logging/isolate.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/logging/style.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/logging/trace.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/logging/user.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/project.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/py.typed +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/rest_client.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/sync.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/audio/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/audio/audio.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/exceptions.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/file/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/file/file.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/file/providers/gcp.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/file/providers/r2.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/file/providers/s3.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/file/types.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/image/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/image/image.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/image/safety_checker.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/kv.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/optimize.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/types.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/utils/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/utils/download_utils.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/utils/endpoint.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/utils/retry.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/video/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/toolkit/video/video.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/utils.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/src/fal/workflows.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/assets/cat.png +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/cli/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/cli/test_apps.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/cli/test_auth.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/cli/test_deploy.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/cli/test_keys.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/cli/test_run.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/cli/test_secrets.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/conftest.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/integration_test.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/mainify_package/__init__.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/mainify_package/impl.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/mainify_package/utils.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/mainify_target.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/test_apps.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/test_kv.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/toolkit/file/providers/test_fal_retry.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/toolkit/file_test.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/toolkit/image_test.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/toolkit/test_types.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tests/toolkit/utils/retry.py +0 -0
- {fal-1.26.3 → fal-1.26.5}/tools/demo_script.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fal
|
|
3
|
-
Version: 1.26.
|
|
3
|
+
Version: 1.26.5
|
|
4
4
|
Summary: fal is an easy-to-use Serverless Python Framework
|
|
5
5
|
Author: Features & Labels <support@fal.ai>
|
|
6
6
|
Requires-Python: >=3.8
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
Requires-Dist: isolate[build]<0.19.0,>=0.18.0
|
|
9
|
-
Requires-Dist: isolate-proto<0.11.0,>=0.10.
|
|
9
|
+
Requires-Dist: isolate-proto<0.11.0,>=0.10.2
|
|
10
10
|
Requires-Dist: grpcio<2,>=1.64.0
|
|
11
11
|
Requires-Dist: dill==0.3.7
|
|
12
12
|
Requires-Dist: cloudpickle==3.0.0
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fal
|
|
3
|
-
Version: 1.26.
|
|
3
|
+
Version: 1.26.5
|
|
4
4
|
Summary: fal is an easy-to-use Serverless Python Framework
|
|
5
5
|
Author: Features & Labels <support@fal.ai>
|
|
6
6
|
Requires-Python: >=3.8
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
Requires-Dist: isolate[build]<0.19.0,>=0.18.0
|
|
9
|
-
Requires-Dist: isolate-proto<0.11.0,>=0.10.
|
|
9
|
+
Requires-Dist: isolate-proto<0.11.0,>=0.10.2
|
|
10
10
|
Requires-Dist: grpcio<2,>=1.64.0
|
|
11
11
|
Requires-Dist: dill==0.3.7
|
|
12
12
|
Requires-Dist: cloudpickle==3.0.0
|
|
@@ -246,7 +246,10 @@ def _add_scale_parser(subparsers, parents):
|
|
|
246
246
|
def _set_rev(args):
|
|
247
247
|
client = get_client(args.host, args.team)
|
|
248
248
|
with client.connect() as connection:
|
|
249
|
-
connection.create_alias(args.app_name, args.app_rev, args.auth)
|
|
249
|
+
alias_info = connection.create_alias(args.app_name, args.app_rev, args.auth)
|
|
250
|
+
table = _apps_table([alias_info])
|
|
251
|
+
|
|
252
|
+
args.console.print(table)
|
|
250
253
|
|
|
251
254
|
|
|
252
255
|
def _add_set_rev_parser(subparsers, parents):
|
|
@@ -23,14 +23,38 @@ def _list(args):
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def _set(args):
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
config = Config()
|
|
27
|
+
|
|
28
|
+
# Check if the profile exists
|
|
29
|
+
if args.PROFILE not in config._config:
|
|
30
|
+
# Profile doesn't exist, offer to create it
|
|
31
|
+
args.console.print(f"Profile [cyan]{args.PROFILE}[/] does not exist.")
|
|
32
|
+
create_profile = input("Would you like to create it? (y/N): ").strip().lower()
|
|
33
|
+
|
|
34
|
+
if create_profile in ["y", "yes"]:
|
|
35
|
+
# Create the profile by setting it
|
|
36
|
+
with config.edit() as config:
|
|
37
|
+
config.set_internal("profile", args.PROFILE)
|
|
38
|
+
config._config[args.PROFILE] = {}
|
|
39
|
+
config._profile = args.PROFILE
|
|
31
40
|
args.console.print(
|
|
32
|
-
"
|
|
41
|
+
f"Profile [cyan]{args.PROFILE}[/] created and set as default."
|
|
33
42
|
)
|
|
43
|
+
else:
|
|
44
|
+
args.console.print("Profile creation cancelled.")
|
|
45
|
+
return
|
|
46
|
+
else:
|
|
47
|
+
# Profile exists, just set it as default
|
|
48
|
+
with config.edit() as config:
|
|
49
|
+
config.set_internal("profile", args.PROFILE)
|
|
50
|
+
config._profile = args.PROFILE
|
|
51
|
+
args.console.print(f"Default profile set to [cyan]{args.PROFILE}[/].")
|
|
52
|
+
|
|
53
|
+
# Check if key is set for the profile
|
|
54
|
+
if not config.get("key"):
|
|
55
|
+
args.console.print(
|
|
56
|
+
"No key set for profile. Use [bold]fal profile key[/] to set a key."
|
|
57
|
+
)
|
|
34
58
|
|
|
35
59
|
|
|
36
60
|
def _unset(args, config: Config | None = None):
|
|
@@ -42,6 +66,8 @@ def _unset(args, config: Config | None = None):
|
|
|
42
66
|
|
|
43
67
|
|
|
44
68
|
def _key_set(args):
|
|
69
|
+
config = Config(validate_profile=True)
|
|
70
|
+
|
|
45
71
|
while True:
|
|
46
72
|
key = input("Enter the key: ")
|
|
47
73
|
if ":" in key:
|
|
@@ -50,7 +76,7 @@ def _key_set(args):
|
|
|
50
76
|
"[red]Invalid key. The key must be in the format [bold]key:value[/].[/]"
|
|
51
77
|
)
|
|
52
78
|
|
|
53
|
-
with
|
|
79
|
+
with config.edit():
|
|
54
80
|
config.set("key", key)
|
|
55
81
|
args.console.print(f"Key set for profile [cyan]{config.profile}[/].")
|
|
56
82
|
|
|
@@ -61,6 +87,24 @@ def _host_set(args):
|
|
|
61
87
|
args.console.print(f"Fal host set to [cyan]{args.HOST}[/].")
|
|
62
88
|
|
|
63
89
|
|
|
90
|
+
def _create(args):
|
|
91
|
+
config = Config()
|
|
92
|
+
|
|
93
|
+
# Check if the profile already exists
|
|
94
|
+
if args.PROFILE in config._config:
|
|
95
|
+
args.console.print(f"Profile [cyan]{args.PROFILE}[/] already exists.")
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
# Create the profile
|
|
99
|
+
with config.edit() as config:
|
|
100
|
+
config._config[args.PROFILE] = {}
|
|
101
|
+
|
|
102
|
+
args.console.print(f"Profile [cyan]{args.PROFILE}[/] created.")
|
|
103
|
+
args.console.print(
|
|
104
|
+
f"Use [bold]fal profile set {args.PROFILE}[/] to set it as default."
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
64
108
|
def _delete(args):
|
|
65
109
|
with Config().edit() as config:
|
|
66
110
|
if config.profile == args.PROFILE:
|
|
@@ -96,7 +140,7 @@ def add_parser(main_subparsers, parents):
|
|
|
96
140
|
)
|
|
97
141
|
list_parser.set_defaults(func=_list)
|
|
98
142
|
|
|
99
|
-
set_help = "Set default profile."
|
|
143
|
+
set_help = "Set default profile. If the profile doesn't exist, you'll be prompted to create it." # noqa: E501
|
|
100
144
|
set_parser = subparsers.add_parser(
|
|
101
145
|
"set",
|
|
102
146
|
description=set_help,
|
|
@@ -140,6 +184,19 @@ def add_parser(main_subparsers, parents):
|
|
|
140
184
|
)
|
|
141
185
|
host_set_parser.set_defaults(func=_host_set)
|
|
142
186
|
|
|
187
|
+
create_help = "Create a new profile. Use 'fal profile set <name>' to set it as default after creation." # noqa: E501
|
|
188
|
+
create_parser = subparsers.add_parser(
|
|
189
|
+
"create",
|
|
190
|
+
description=create_help,
|
|
191
|
+
help=create_help,
|
|
192
|
+
parents=parents,
|
|
193
|
+
)
|
|
194
|
+
create_parser.add_argument(
|
|
195
|
+
"PROFILE",
|
|
196
|
+
help="Profile name.",
|
|
197
|
+
)
|
|
198
|
+
create_parser.set_defaults(func=_create)
|
|
199
|
+
|
|
143
200
|
delete_help = "Delete profile."
|
|
144
201
|
delete_parser = subparsers.add_parser(
|
|
145
202
|
"delete",
|
|
@@ -4,8 +4,9 @@ import os
|
|
|
4
4
|
from contextlib import contextmanager
|
|
5
5
|
from typing import Dict, Iterator, List, Optional
|
|
6
6
|
|
|
7
|
-
SETTINGS_SECTION = "__internal__"
|
|
8
|
-
|
|
7
|
+
SETTINGS_SECTION = "__internal__"
|
|
8
|
+
|
|
9
|
+
NO_PROFILE_ERROR = ValueError("No profile set.")
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class Config:
|
|
@@ -15,7 +16,7 @@ class Config:
|
|
|
15
16
|
|
|
16
17
|
DEFAULT_CONFIG_PATH = "~/.fal/config.toml"
|
|
17
18
|
|
|
18
|
-
def __init__(self):
|
|
19
|
+
def __init__(self, *, validate_profile: bool = False):
|
|
19
20
|
import tomli
|
|
20
21
|
|
|
21
22
|
self.config_path = os.path.expanduser(
|
|
@@ -28,11 +29,17 @@ class Config:
|
|
|
28
29
|
except FileNotFoundError:
|
|
29
30
|
self._config = {}
|
|
30
31
|
|
|
31
|
-
profile = (
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
profile = os.getenv("FAL_PROFILE") or self.get_internal("profile")
|
|
33
|
+
|
|
34
|
+
# Try to set the profile, but don't fail if it doesn't exist
|
|
35
|
+
try:
|
|
36
|
+
self.profile = profile
|
|
37
|
+
except ValueError:
|
|
38
|
+
# Profile doesn't exist, set to None
|
|
39
|
+
self._profile = None
|
|
34
40
|
|
|
35
|
-
self.profile
|
|
41
|
+
if validate_profile and not self.profile:
|
|
42
|
+
raise NO_PROFILE_ERROR
|
|
36
43
|
|
|
37
44
|
@property
|
|
38
45
|
def profile(self) -> Optional[str]:
|
|
@@ -41,9 +48,10 @@ class Config:
|
|
|
41
48
|
@profile.setter
|
|
42
49
|
def profile(self, value: Optional[str]) -> None:
|
|
43
50
|
if value and value not in self._config:
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
# Don't automatically create profiles - they should be created explicitly
|
|
52
|
+
raise ValueError(
|
|
53
|
+
f"Profile '{value}' does not exist. Create it first or use the profile set command." # noqa: E501
|
|
54
|
+
)
|
|
47
55
|
elif not value:
|
|
48
56
|
self.unset_internal("profile")
|
|
49
57
|
|
|
@@ -53,11 +61,7 @@ class Config:
|
|
|
53
61
|
keys: List[str] = []
|
|
54
62
|
for key in self._config:
|
|
55
63
|
if key != SETTINGS_SECTION:
|
|
56
|
-
|
|
57
|
-
# Add it at the beginning
|
|
58
|
-
keys.insert(0, key)
|
|
59
|
-
else:
|
|
60
|
-
keys.append(key)
|
|
64
|
+
keys.append(key)
|
|
61
65
|
|
|
62
66
|
return keys
|
|
63
67
|
|
|
@@ -75,13 +79,13 @@ class Config:
|
|
|
75
79
|
|
|
76
80
|
def set(self, key: str, value: str) -> None:
|
|
77
81
|
if not self.profile:
|
|
78
|
-
raise
|
|
82
|
+
raise NO_PROFILE_ERROR
|
|
79
83
|
|
|
80
84
|
self._config[self.profile][key] = value
|
|
81
85
|
|
|
82
86
|
def unset(self, key: str) -> None:
|
|
83
87
|
if not self.profile:
|
|
84
|
-
raise
|
|
88
|
+
raise NO_PROFILE_ERROR
|
|
85
89
|
|
|
86
90
|
self._config.get(self.profile, {}).pop(key, None)
|
|
87
91
|
|
|
@@ -56,6 +56,16 @@ class FalFileSystem(AbstractFileSystem):
|
|
|
56
56
|
raise FalServerlessException(detail)
|
|
57
57
|
return response
|
|
58
58
|
|
|
59
|
+
def _abspath(self, rpath):
|
|
60
|
+
if rpath.startswith("/"):
|
|
61
|
+
return rpath
|
|
62
|
+
|
|
63
|
+
cwd = "/data"
|
|
64
|
+
if rpath in [".", ""]:
|
|
65
|
+
return cwd
|
|
66
|
+
|
|
67
|
+
return posixpath.join(cwd, rpath)
|
|
68
|
+
|
|
59
69
|
def _ls(self, path):
|
|
60
70
|
response = self._request("GET", f"/files/list/{path}")
|
|
61
71
|
files = response.json()
|
|
@@ -73,7 +83,7 @@ class FalFileSystem(AbstractFileSystem):
|
|
|
73
83
|
)
|
|
74
84
|
|
|
75
85
|
def ls(self, path, detail=True, **kwargs):
|
|
76
|
-
abs_path =
|
|
86
|
+
abs_path = self._abspath(path)
|
|
77
87
|
if abs_path in self.dircache:
|
|
78
88
|
entries = self.dircache[abs_path]
|
|
79
89
|
elif abs_path in ["/", "", "."]:
|
|
@@ -95,20 +105,29 @@ class FalFileSystem(AbstractFileSystem):
|
|
|
95
105
|
return [entry["name"] for entry in entries]
|
|
96
106
|
|
|
97
107
|
def info(self, path, **kwargs):
|
|
98
|
-
|
|
108
|
+
abs_path = self._abspath(path)
|
|
109
|
+
if abs_path == "/":
|
|
110
|
+
return {
|
|
111
|
+
"name": "/",
|
|
112
|
+
"size": 0,
|
|
113
|
+
"type": "directory",
|
|
114
|
+
"mtime": 0,
|
|
115
|
+
}
|
|
116
|
+
parent = posixpath.dirname(abs_path)
|
|
99
117
|
entries = self.ls(parent, detail=True)
|
|
100
118
|
for entry in entries:
|
|
101
|
-
if entry["name"] ==
|
|
119
|
+
if entry["name"] == abs_path:
|
|
102
120
|
return entry
|
|
103
|
-
raise FileNotFoundError(f"File not found: {
|
|
121
|
+
raise FileNotFoundError(f"File not found: {abs_path}")
|
|
104
122
|
|
|
105
123
|
def get_file(self, rpath, lpath, **kwargs):
|
|
106
|
-
|
|
124
|
+
abs_rpath = self._abspath(rpath)
|
|
125
|
+
if self.isdir(abs_rpath):
|
|
107
126
|
os.makedirs(lpath, exist_ok=True)
|
|
108
127
|
return
|
|
109
128
|
|
|
110
129
|
with open(lpath, "wb") as fobj:
|
|
111
|
-
response = self._request("GET", f"/files/file/{
|
|
130
|
+
response = self._request("GET", f"/files/file/{abs_rpath}")
|
|
112
131
|
fobj.write(response.content)
|
|
113
132
|
|
|
114
133
|
def _put_file_part(self, rpath, lpath, upload_id, part_number, chunk_size):
|
|
@@ -177,6 +196,8 @@ class FalFileSystem(AbstractFileSystem):
|
|
|
177
196
|
if os.path.isdir(lpath):
|
|
178
197
|
return
|
|
179
198
|
|
|
199
|
+
abs_rpath = self._abspath(rpath)
|
|
200
|
+
|
|
180
201
|
size = os.path.getsize(lpath)
|
|
181
202
|
with Progress(
|
|
182
203
|
SpinnerColumn(),
|
|
@@ -185,29 +206,31 @@ class FalFileSystem(AbstractFileSystem):
|
|
|
185
206
|
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
186
207
|
) as progress:
|
|
187
208
|
if size > MULTIPART_THRESHOLD:
|
|
188
|
-
self._put_file_multipart(lpath,
|
|
209
|
+
self._put_file_multipart(lpath, abs_rpath, size, progress)
|
|
189
210
|
else:
|
|
190
211
|
task = progress.add_task(f"{os.path.basename(lpath)}", total=1)
|
|
191
212
|
with open(lpath, "rb") as fobj:
|
|
192
213
|
self._request(
|
|
193
214
|
"POST",
|
|
194
|
-
f"/files/file/local/{
|
|
215
|
+
f"/files/file/local/{abs_rpath}",
|
|
195
216
|
files={"file_upload": (posixpath.basename(lpath), fobj)},
|
|
196
217
|
)
|
|
197
218
|
progress.advance(task)
|
|
198
219
|
self.dircache.clear()
|
|
199
220
|
|
|
200
221
|
def put_file_from_url(self, url, rpath, mode="overwrite", **kwargs):
|
|
222
|
+
abs_rpath = self._abspath(rpath)
|
|
201
223
|
self._request(
|
|
202
224
|
"POST",
|
|
203
|
-
f"/files/file/url/{
|
|
225
|
+
f"/files/file/url/{abs_rpath}",
|
|
204
226
|
json={"url": url},
|
|
205
227
|
)
|
|
206
228
|
self.dircache.clear()
|
|
207
229
|
|
|
208
230
|
def rm(self, path, **kwargs):
|
|
231
|
+
abs_path = self._abspath(path)
|
|
209
232
|
self._request(
|
|
210
233
|
"DELETE",
|
|
211
|
-
f"/files/file/{
|
|
234
|
+
f"/files/file/{abs_path}",
|
|
212
235
|
)
|
|
213
236
|
self.dircache.clear()
|
|
@@ -735,7 +735,7 @@ class FalServerlessConnection:
|
|
|
735
735
|
alias: str,
|
|
736
736
|
revision: str,
|
|
737
737
|
auth_mode: AuthMode,
|
|
738
|
-
):
|
|
738
|
+
) -> AliasInfo:
|
|
739
739
|
if auth_mode == "public":
|
|
740
740
|
auth = isolate_proto.ApplicationAuthMode.PUBLIC
|
|
741
741
|
elif auth_mode == "shared":
|
|
@@ -750,7 +750,8 @@ class FalServerlessConnection:
|
|
|
750
750
|
revision=revision,
|
|
751
751
|
auth_mode=auth,
|
|
752
752
|
)
|
|
753
|
-
self.stub.SetAlias(request)
|
|
753
|
+
res = self.stub.SetAlias(request)
|
|
754
|
+
return from_grpc(res.alias_info)
|
|
754
755
|
|
|
755
756
|
def delete_alias(self, alias: str) -> str | None:
|
|
756
757
|
request = isolate_proto.DeleteAliasRequest(alias=alias)
|
|
@@ -9,7 +9,7 @@ from contextlib import contextmanager
|
|
|
9
9
|
from dataclasses import dataclass
|
|
10
10
|
from datetime import datetime, timezone
|
|
11
11
|
from pathlib import Path
|
|
12
|
-
from typing import Any, Generator, Generic, TypeVar
|
|
12
|
+
from typing import Any, Dict, Generator, Generic, TypeVar
|
|
13
13
|
from urllib.error import HTTPError
|
|
14
14
|
from urllib.parse import urlparse, urlunparse
|
|
15
15
|
from urllib.request import Request, urlopen
|
|
@@ -69,6 +69,19 @@ def _maybe_retry_request(
|
|
|
69
69
|
yield response
|
|
70
70
|
|
|
71
71
|
|
|
72
|
+
def _object_lifecycle_headers(
|
|
73
|
+
headers: dict[str, str],
|
|
74
|
+
object_lifecycle_preference: dict[str, str] | None,
|
|
75
|
+
):
|
|
76
|
+
if object_lifecycle_preference:
|
|
77
|
+
# Used by V3 CDN
|
|
78
|
+
headers["X-Fal-Object-Lifecycle"] = json.dumps(object_lifecycle_preference)
|
|
79
|
+
# Used by V1 CDN
|
|
80
|
+
headers["X-Fal-Object-Lifecycle-Preference"] = json.dumps(
|
|
81
|
+
object_lifecycle_preference
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
72
85
|
@dataclass
|
|
73
86
|
class FalV2Token:
|
|
74
87
|
token: str
|
|
@@ -451,14 +464,6 @@ class MultipartUploadGCS:
|
|
|
451
464
|
|
|
452
465
|
@dataclass
|
|
453
466
|
class FalFileRepository(FalFileRepositoryBase):
|
|
454
|
-
def _object_lifecycle_headers(
|
|
455
|
-
self,
|
|
456
|
-
headers: dict[str, str],
|
|
457
|
-
object_lifecycle_preference: dict[str, str] | None,
|
|
458
|
-
):
|
|
459
|
-
if object_lifecycle_preference:
|
|
460
|
-
headers["X-Fal-Object-Lifecycle"] = json.dumps(object_lifecycle_preference)
|
|
461
|
-
|
|
462
467
|
def save(
|
|
463
468
|
self,
|
|
464
469
|
file: FileData,
|
|
@@ -479,9 +484,8 @@ class FalFileRepository(FalFileRepositoryBase):
|
|
|
479
484
|
max_concurrency=multipart_max_concurrency,
|
|
480
485
|
)
|
|
481
486
|
|
|
482
|
-
headers = {}
|
|
483
|
-
|
|
484
|
-
headers["X-Fal-Object-Lifecycle"] = json.dumps(object_lifecycle_preference)
|
|
487
|
+
headers: Dict[str, str] = {}
|
|
488
|
+
_object_lifecycle_headers(headers, object_lifecycle_preference)
|
|
485
489
|
|
|
486
490
|
return self._save(file, "gcs", headers=headers)
|
|
487
491
|
|
|
@@ -1124,6 +1128,8 @@ class FalFileRepositoryV2(FalFileRepositoryBase):
|
|
|
1124
1128
|
"Content-Type": file.content_type,
|
|
1125
1129
|
}
|
|
1126
1130
|
|
|
1131
|
+
_object_lifecycle_headers(headers, object_lifecycle_preference)
|
|
1132
|
+
|
|
1127
1133
|
storage_url = f"{token.base_upload_url}/upload"
|
|
1128
1134
|
|
|
1129
1135
|
try:
|
|
@@ -1195,16 +1201,6 @@ class InMemoryRepository(FileRepository):
|
|
|
1195
1201
|
|
|
1196
1202
|
@dataclass
|
|
1197
1203
|
class FalCDNFileRepository(FileRepository):
|
|
1198
|
-
def _object_lifecycle_headers(
|
|
1199
|
-
self,
|
|
1200
|
-
headers: dict[str, str],
|
|
1201
|
-
object_lifecycle_preference: dict[str, str] | None,
|
|
1202
|
-
):
|
|
1203
|
-
if object_lifecycle_preference:
|
|
1204
|
-
headers["X-Fal-Object-Lifecycle-Preference"] = json.dumps(
|
|
1205
|
-
object_lifecycle_preference
|
|
1206
|
-
)
|
|
1207
|
-
|
|
1208
1204
|
def save(
|
|
1209
1205
|
self,
|
|
1210
1206
|
file: FileData,
|
|
@@ -1221,7 +1217,7 @@ class FalCDNFileRepository(FileRepository):
|
|
|
1221
1217
|
"X-Fal-File-Name": file.file_name,
|
|
1222
1218
|
}
|
|
1223
1219
|
|
|
1224
|
-
|
|
1220
|
+
_object_lifecycle_headers(headers, object_lifecycle_preference)
|
|
1225
1221
|
|
|
1226
1222
|
url = os.getenv("FAL_CDN_HOST", _FAL_CDN) + "/files/upload"
|
|
1227
1223
|
request = Request(url, headers=headers, method="POST", data=file.data)
|
|
@@ -1288,6 +1284,7 @@ class FalFileRepositoryV3(FileRepository):
|
|
|
1288
1284
|
"Accept": "application/json",
|
|
1289
1285
|
"Content-Type": "application/json",
|
|
1290
1286
|
}
|
|
1287
|
+
_object_lifecycle_headers(headers, object_lifecycle_preference)
|
|
1291
1288
|
|
|
1292
1289
|
grpc_host = os.environ.get("FAL_HOST", "api.alpha.fal.ai")
|
|
1293
1290
|
rest_host = grpc_host.replace("api", "rest", 1)
|
|
@@ -1376,14 +1373,6 @@ class InternalFalFileRepositoryV3(FileRepository):
|
|
|
1376
1373
|
That way it can avoid the need to refresh the token for every upload.
|
|
1377
1374
|
"""
|
|
1378
1375
|
|
|
1379
|
-
def _object_lifecycle_headers(
|
|
1380
|
-
self,
|
|
1381
|
-
headers: dict[str, str],
|
|
1382
|
-
object_lifecycle_preference: dict[str, str] | None,
|
|
1383
|
-
):
|
|
1384
|
-
if object_lifecycle_preference:
|
|
1385
|
-
headers["X-Fal-Object-Lifecycle"] = json.dumps(object_lifecycle_preference)
|
|
1386
|
-
|
|
1387
1376
|
def save(
|
|
1388
1377
|
self,
|
|
1389
1378
|
file: FileData,
|
|
@@ -1413,7 +1402,7 @@ class InternalFalFileRepositoryV3(FileRepository):
|
|
|
1413
1402
|
"X-Fal-File-Name": file.file_name,
|
|
1414
1403
|
}
|
|
1415
1404
|
|
|
1416
|
-
|
|
1405
|
+
_object_lifecycle_headers(headers, object_lifecycle_preference)
|
|
1417
1406
|
|
|
1418
1407
|
url = os.getenv("FAL_CDN_V3_HOST", _FAL_CDN_V3) + "/files/upload"
|
|
1419
1408
|
request = Request(url, headers=headers, method="POST", data=file.data)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import posixpath
|
|
2
|
+
import uuid
|
|
3
|
+
|
|
4
|
+
from fal.files import FalFileSystem
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_fal_fs(tmp_path):
|
|
8
|
+
(tmp_path / "myfile").write_text("myfile")
|
|
9
|
+
(tmp_path / "mydir").mkdir()
|
|
10
|
+
(tmp_path / "mydir" / "myfile1").write_text("myfile1")
|
|
11
|
+
(tmp_path / "mydir" / "myfile2").write_text("myfile2")
|
|
12
|
+
|
|
13
|
+
(tmp_path / "downloaded").mkdir()
|
|
14
|
+
|
|
15
|
+
remote_temp_dir = f"/data/tmp/{uuid.uuid4()}"
|
|
16
|
+
|
|
17
|
+
fs = FalFileSystem()
|
|
18
|
+
|
|
19
|
+
assert fs.exists("/")
|
|
20
|
+
assert fs.isdir("/")
|
|
21
|
+
assert fs.exists("/data")
|
|
22
|
+
assert fs.isdir("/data")
|
|
23
|
+
assert fs.exists(".")
|
|
24
|
+
assert fs.isdir(".")
|
|
25
|
+
|
|
26
|
+
fs.put(str(tmp_path / "myfile"), remote_temp_dir + "/myfile")
|
|
27
|
+
assert fs.exists(remote_temp_dir + "/myfile")
|
|
28
|
+
assert fs.isdir(remote_temp_dir)
|
|
29
|
+
assert fs.isfile(remote_temp_dir + "/myfile")
|
|
30
|
+
assert not fs.isdir(remote_temp_dir + "/myfile")
|
|
31
|
+
fs.get(remote_temp_dir + "/myfile", str(tmp_path / "downloaded" / "myfile"))
|
|
32
|
+
assert (tmp_path / "downloaded" / "myfile").read_text() == "myfile"
|
|
33
|
+
|
|
34
|
+
fs.put(str(tmp_path / "mydir"), remote_temp_dir + "/mydir", recursive=True)
|
|
35
|
+
assert fs.exists(remote_temp_dir + "/mydir")
|
|
36
|
+
assert fs.isdir(remote_temp_dir + "/mydir")
|
|
37
|
+
assert fs.isfile(remote_temp_dir + "/mydir/myfile1")
|
|
38
|
+
assert fs.isfile(remote_temp_dir + "/mydir/myfile2")
|
|
39
|
+
fs.get(
|
|
40
|
+
remote_temp_dir + "/mydir",
|
|
41
|
+
str(tmp_path / "downloaded" / "mydir"),
|
|
42
|
+
recursive=True,
|
|
43
|
+
)
|
|
44
|
+
assert (tmp_path / "downloaded" / "mydir" / "myfile1").read_text() == "myfile1"
|
|
45
|
+
assert (tmp_path / "downloaded" / "mydir" / "myfile2").read_text() == "myfile2"
|
|
46
|
+
|
|
47
|
+
assert fs.ls(remote_temp_dir, detail=False) == [
|
|
48
|
+
remote_temp_dir + "/mydir",
|
|
49
|
+
remote_temp_dir + "/myfile",
|
|
50
|
+
]
|
|
51
|
+
assert fs.ls(posixpath.relpath(remote_temp_dir, "/data"), detail=False) == [
|
|
52
|
+
remote_temp_dir + "/mydir",
|
|
53
|
+
remote_temp_dir + "/myfile",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
fs.rm(remote_temp_dir + "/myfile")
|
|
57
|
+
assert not fs.exists(remote_temp_dir + "/myfile")
|
|
58
|
+
fs.rm(remote_temp_dir + "/mydir", recursive=True)
|
|
59
|
+
assert not fs.exists(remote_temp_dir + "/mydir")
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import subprocess
|
|
4
|
+
import uuid
|
|
4
5
|
from contextlib import suppress
|
|
5
6
|
|
|
6
7
|
import pytest
|
|
@@ -82,6 +83,44 @@ def test_regular_function_in_a_container(isolated_client):
|
|
|
82
83
|
assert mult(5, 2) == 10
|
|
83
84
|
|
|
84
85
|
|
|
86
|
+
def test_container_no_venv(isolated_client):
|
|
87
|
+
actual_python = active_python()
|
|
88
|
+
|
|
89
|
+
@isolated_client(
|
|
90
|
+
"container",
|
|
91
|
+
image=ContainerImage.from_dockerfile_str(
|
|
92
|
+
f"""
|
|
93
|
+
FROM python:{actual_python}
|
|
94
|
+
# {uuid.uuid4()}
|
|
95
|
+
"""
|
|
96
|
+
),
|
|
97
|
+
)
|
|
98
|
+
def myfunc():
|
|
99
|
+
return 42
|
|
100
|
+
|
|
101
|
+
assert myfunc() == 42
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_container_venv(isolated_client):
|
|
105
|
+
actual_python = active_python()
|
|
106
|
+
|
|
107
|
+
@isolated_client(
|
|
108
|
+
"container",
|
|
109
|
+
image=ContainerImage.from_dockerfile_str(
|
|
110
|
+
f"""
|
|
111
|
+
FROM python:{actual_python}
|
|
112
|
+
# {uuid.uuid4()}
|
|
113
|
+
RUN python -m venv .venv
|
|
114
|
+
ENV PATH="/root/.venv/bin:$PATH"
|
|
115
|
+
"""
|
|
116
|
+
),
|
|
117
|
+
)
|
|
118
|
+
def myfunc():
|
|
119
|
+
return 42
|
|
120
|
+
|
|
121
|
+
assert myfunc() == 42
|
|
122
|
+
|
|
123
|
+
|
|
85
124
|
def test_regular_function_in_a_container_with_custom_image(isolated_client):
|
|
86
125
|
actual_python = active_python()
|
|
87
126
|
|
|
@@ -584,7 +623,7 @@ def test_worker_env_vars(isolated_client):
|
|
|
584
623
|
[
|
|
585
624
|
(
|
|
586
625
|
"fal",
|
|
587
|
-
"https://storage.googleapis.com/isolate-dev-smiling-
|
|
626
|
+
"https://storage.googleapis.com/isolate-dev-smiling-shark_toolkit_public_bucket/",
|
|
588
627
|
),
|
|
589
628
|
("fal_v2", "https://v2.fal.media/files"),
|
|
590
629
|
("fal_v3", "https://v3.fal.media/files"),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|