fal 1.14.0__tar.gz → 1.15.1__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.14.0/fal.egg-info → fal-1.15.1}/PKG-INFO +2 -1
- {fal-1.14.0 → fal-1.15.1/fal.egg-info}/PKG-INFO +2 -1
- {fal-1.14.0 → fal-1.15.1}/fal.egg-info/SOURCES.txt +1 -0
- {fal-1.14.0 → fal-1.15.1}/fal.egg-info/requires.txt +1 -0
- {fal-1.14.0 → fal-1.15.1}/pyproject.toml +1 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/_fal_version.py +2 -2
- fal-1.15.1/src/fal/cli/files.py +70 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/main.py +2 -0
- fal-1.15.1/src/fal/files.py +90 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/file/providers/fal.py +35 -22
- {fal-1.14.0 → fal-1.15.1}/tests/cli/test_deploy.py +1 -1
- {fal-1.14.0 → fal-1.15.1}/tests/cli/test_run.py +1 -1
- {fal-1.14.0 → fal-1.15.1}/tests/test_apps.py +3 -0
- fal-1.14.0/src/fal/files.py +0 -81
- {fal-1.14.0 → fal-1.15.1}/.gitignore +0 -0
- {fal-1.14.0 → fal-1.15.1}/Makefile +0 -0
- {fal-1.14.0 → fal-1.15.1}/README.md +0 -0
- {fal-1.14.0 → fal-1.15.1}/docs/conf.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/docs/index.rst +0 -0
- {fal-1.14.0 → fal-1.15.1}/fal.egg-info/dependency_links.txt +0 -0
- {fal-1.14.0 → fal-1.15.1}/fal.egg-info/entry_points.txt +0 -0
- {fal-1.14.0 → fal-1.15.1}/fal.egg-info/top_level.txt +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/README.md +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/pyproject.toml +0 -0
- {fal-1.14.0 → fal-1.15.1}/openapi_rest.config.yaml +0 -0
- {fal-1.14.0 → fal-1.15.1}/setup.cfg +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/__main__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/_serialization.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/_version.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/api.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/app.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/apps.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/auth/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/auth/auth0.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/auth/local.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/_utils.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/api.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/apps.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/auth.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/cli_nested_json.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/create.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/debug.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/deploy.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/doctor.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/keys.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/parser.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/profile.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/run.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/runners.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/secrets.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/cli/teams.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/config.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/console/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/console/icons.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/console/ux.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/container.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/exceptions/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/exceptions/_base.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/exceptions/_cuda.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/exceptions/auth.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/flags.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/logging/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/logging/isolate.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/logging/style.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/logging/trace.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/logging/user.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/project.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/py.typed +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/rest_client.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/sdk.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/sync.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/exceptions.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/file/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/file/file.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/file/providers/gcp.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/file/providers/r2.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/file/providers/s3.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/file/types.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/image/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/image/image.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/image/safety_checker.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/optimize.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/types.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/utils/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/utils/download_utils.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/toolkit/utils/retry.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/utils.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/src/fal/workflows.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/assets/cat.png +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/cli/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/cli/test_apps.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/cli/test_auth.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/cli/test_keys.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/cli/test_secrets.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/conftest.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/integration_test.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/mainify_package/__init__.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/mainify_package/impl.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/mainify_package/utils.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/mainify_target.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/test_stability.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/toolkit/file_test.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/toolkit/image_test.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/toolkit/test_types.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tests/toolkit/utils/retry.py +0 -0
- {fal-1.14.0 → fal-1.15.1}/tools/demo_script.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fal
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.15.1
|
|
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
|
|
@@ -38,6 +38,7 @@ Requires-Dist: uvicorn<1,>=0.29.0
|
|
|
38
38
|
Requires-Dist: cookiecutter
|
|
39
39
|
Requires-Dist: tomli<3,>2
|
|
40
40
|
Requires-Dist: tomli-w<2,>=1
|
|
41
|
+
Requires-Dist: fsspec
|
|
41
42
|
Provides-Extra: docs
|
|
42
43
|
Requires-Dist: sphinx<8.2.0; extra == "docs"
|
|
43
44
|
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fal
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.15.1
|
|
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
|
|
@@ -38,6 +38,7 @@ Requires-Dist: uvicorn<1,>=0.29.0
|
|
|
38
38
|
Requires-Dist: cookiecutter
|
|
39
39
|
Requires-Dist: tomli<3,>2
|
|
40
40
|
Requires-Dist: tomli-w<2,>=1
|
|
41
|
+
Requires-Dist: fsspec
|
|
41
42
|
Provides-Extra: docs
|
|
42
43
|
Requires-Dist: sphinx<8.2.0; extra == "docs"
|
|
43
44
|
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from .parser import FalClientParser
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _list(args):
|
|
5
|
+
import posixpath
|
|
6
|
+
|
|
7
|
+
from fal.files import FalFileSystem
|
|
8
|
+
|
|
9
|
+
fs = FalFileSystem()
|
|
10
|
+
|
|
11
|
+
for entry in fs.ls(args.path, detail=True):
|
|
12
|
+
name = posixpath.basename(entry["name"])
|
|
13
|
+
color = "blue" if entry["type"] == "directory" else "default"
|
|
14
|
+
args.console.print(f"[{color}]{name}[/{color}]")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _download(args):
|
|
18
|
+
from fal.files import FalFileSystem
|
|
19
|
+
|
|
20
|
+
fs = FalFileSystem()
|
|
21
|
+
fs.get(args.remote_path, args.local_path, recursive=True)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _upload(args):
|
|
25
|
+
from fal.files import FalFileSystem
|
|
26
|
+
|
|
27
|
+
fs = FalFileSystem()
|
|
28
|
+
fs.put(args.local_path, args.remote_path, recursive=True)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def add_parser(main_subparsers, parents):
|
|
32
|
+
files_help = "Manage fal files."
|
|
33
|
+
parser = main_subparsers.add_parser(
|
|
34
|
+
"files",
|
|
35
|
+
aliases=["file"],
|
|
36
|
+
description=files_help,
|
|
37
|
+
help=files_help,
|
|
38
|
+
parents=parents,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
subparsers = parser.add_subparsers(
|
|
42
|
+
title="Commands",
|
|
43
|
+
metavar="command",
|
|
44
|
+
required=True,
|
|
45
|
+
parser_class=FalClientParser,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
list_parser = subparsers.add_parser("list", aliases=["ls"], parents=parents)
|
|
49
|
+
list_parser.add_argument(
|
|
50
|
+
"path",
|
|
51
|
+
nargs="?",
|
|
52
|
+
type=str,
|
|
53
|
+
help="The path to list",
|
|
54
|
+
default="/",
|
|
55
|
+
)
|
|
56
|
+
list_parser.set_defaults(func=_list)
|
|
57
|
+
|
|
58
|
+
download_parser = subparsers.add_parser("download", parents=parents)
|
|
59
|
+
download_parser.add_argument(
|
|
60
|
+
"remote_path", type=str, help="Remote path to download"
|
|
61
|
+
)
|
|
62
|
+
download_parser.add_argument(
|
|
63
|
+
"local_path", type=str, help="Local path to download to"
|
|
64
|
+
)
|
|
65
|
+
download_parser.set_defaults(func=_download)
|
|
66
|
+
|
|
67
|
+
upload_parser = subparsers.add_parser("upload", parents=parents)
|
|
68
|
+
upload_parser.add_argument("local_path", type=str, help="Local path to upload")
|
|
69
|
+
upload_parser.add_argument("remote_path", type=str, help="Remote path to upload to")
|
|
70
|
+
upload_parser.set_defaults(func=_upload)
|
|
@@ -13,6 +13,7 @@ from . import (
|
|
|
13
13
|
create,
|
|
14
14
|
deploy,
|
|
15
15
|
doctor,
|
|
16
|
+
files,
|
|
16
17
|
keys,
|
|
17
18
|
profile,
|
|
18
19
|
run,
|
|
@@ -57,6 +58,7 @@ def _get_main_parser() -> argparse.ArgumentParser:
|
|
|
57
58
|
create,
|
|
58
59
|
runners,
|
|
59
60
|
teams,
|
|
61
|
+
files,
|
|
60
62
|
]:
|
|
61
63
|
cmd.add_parser(subparsers, parents)
|
|
62
64
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import posixpath
|
|
3
|
+
from functools import cached_property
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from fsspec import AbstractFileSystem
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
import httpx
|
|
10
|
+
|
|
11
|
+
USER_AGENT = "fal-sdk/1.14.0 (python)"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class FalFileSystem(AbstractFileSystem):
|
|
15
|
+
@cached_property
|
|
16
|
+
def _client(self) -> "httpx.Client":
|
|
17
|
+
from httpx import Client
|
|
18
|
+
|
|
19
|
+
from fal.flags import REST_URL
|
|
20
|
+
from fal.sdk import get_default_credentials
|
|
21
|
+
|
|
22
|
+
creds = get_default_credentials()
|
|
23
|
+
return Client(
|
|
24
|
+
base_url=REST_URL,
|
|
25
|
+
headers={
|
|
26
|
+
**creds.to_headers(),
|
|
27
|
+
"User-Agent": USER_AGENT,
|
|
28
|
+
},
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def ls(self, path, detail=True, **kwargs):
|
|
32
|
+
if path in self.dircache:
|
|
33
|
+
entries = self.dircache[path]
|
|
34
|
+
else:
|
|
35
|
+
response = self._client.get(f"/files/list/{path.lstrip('/')}")
|
|
36
|
+
response.raise_for_status()
|
|
37
|
+
files = response.json()
|
|
38
|
+
entries = sorted(
|
|
39
|
+
(
|
|
40
|
+
{
|
|
41
|
+
"name": entry["path"].lstrip("/data/"),
|
|
42
|
+
"size": entry["size"],
|
|
43
|
+
"type": "file" if entry["is_file"] else "directory",
|
|
44
|
+
"mtime": entry["updated_time"],
|
|
45
|
+
}
|
|
46
|
+
for entry in files
|
|
47
|
+
),
|
|
48
|
+
key=lambda x: x["name"],
|
|
49
|
+
)
|
|
50
|
+
self.dircache[path] = entries
|
|
51
|
+
|
|
52
|
+
if detail:
|
|
53
|
+
return entries
|
|
54
|
+
|
|
55
|
+
return [entry["name"] for entry in entries]
|
|
56
|
+
|
|
57
|
+
def info(self, path, **kwargs):
|
|
58
|
+
parent = posixpath.dirname(path)
|
|
59
|
+
entries = self.ls(parent, detail=True)
|
|
60
|
+
for entry in entries:
|
|
61
|
+
if entry["name"] == path:
|
|
62
|
+
return entry
|
|
63
|
+
raise FileNotFoundError(f"File not found: {path}")
|
|
64
|
+
|
|
65
|
+
def get_file(self, rpath, lpath, **kwargs):
|
|
66
|
+
if self.isdir(rpath):
|
|
67
|
+
os.makedirs(lpath, exist_ok=True)
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
with open(lpath, "wb") as fobj:
|
|
71
|
+
response = self._client.get(f"/files/file/{rpath.lstrip('/')}")
|
|
72
|
+
response.raise_for_status()
|
|
73
|
+
fobj.write(response.content)
|
|
74
|
+
|
|
75
|
+
def put_file(self, lpath, rpath, mode="overwrite", **kwargs):
|
|
76
|
+
if os.path.isdir(lpath):
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
with open(lpath, "rb") as fobj:
|
|
80
|
+
response = self._client.post(
|
|
81
|
+
f"/files/file/local/{rpath.lstrip('/')}",
|
|
82
|
+
files={"file_upload": (posixpath.basename(lpath), fobj, "text/plain")},
|
|
83
|
+
)
|
|
84
|
+
response.raise_for_status()
|
|
85
|
+
self.dircache.clear()
|
|
86
|
+
|
|
87
|
+
def rm(self, path, **kwargs):
|
|
88
|
+
response = self._client.delete(f"/files/file/{path.lstrip('/')}")
|
|
89
|
+
response.raise_for_status()
|
|
90
|
+
self.dircache.clear()
|
|
@@ -5,13 +5,15 @@ import math
|
|
|
5
5
|
import os
|
|
6
6
|
import threading
|
|
7
7
|
from base64 import b64encode
|
|
8
|
+
from contextlib import contextmanager
|
|
8
9
|
from dataclasses import dataclass
|
|
9
10
|
from datetime import datetime, timezone
|
|
10
11
|
from pathlib import Path
|
|
11
|
-
from typing import Generic, TypeVar
|
|
12
|
+
from typing import Generator, Generic, TypeVar
|
|
12
13
|
from urllib.error import HTTPError
|
|
13
14
|
from urllib.parse import urlparse, urlunparse
|
|
14
15
|
from urllib.request import Request, urlopen
|
|
16
|
+
from urllib.response import addinfourl
|
|
15
17
|
|
|
16
18
|
from fal.auth import key_credentials
|
|
17
19
|
from fal.toolkit.exceptions import FileUploadException
|
|
@@ -21,6 +23,17 @@ from fal.toolkit.utils.retry import retry
|
|
|
21
23
|
_FAL_CDN = "https://fal.media"
|
|
22
24
|
_FAL_CDN_V3 = "https://v3.fal.media"
|
|
23
25
|
|
|
26
|
+
DEFAULT_REQUEST_TIMEOUT = 10
|
|
27
|
+
PUT_REQUEST_TIMEOUT = 5 * 60
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@contextmanager
|
|
31
|
+
def _urlopen(
|
|
32
|
+
request: Request, timeout: int = DEFAULT_REQUEST_TIMEOUT
|
|
33
|
+
) -> Generator[addinfourl, None, None]:
|
|
34
|
+
with urlopen(request, timeout=timeout) as response:
|
|
35
|
+
yield response
|
|
36
|
+
|
|
24
37
|
|
|
25
38
|
@dataclass
|
|
26
39
|
class FalV2Token:
|
|
@@ -79,7 +92,7 @@ class FalV2TokenManager:
|
|
|
79
92
|
data=b"{}",
|
|
80
93
|
method="POST",
|
|
81
94
|
)
|
|
82
|
-
with
|
|
95
|
+
with _urlopen(req) as response:
|
|
83
96
|
result = json.load(response)
|
|
84
97
|
|
|
85
98
|
parsed_base_url = urlparse(result["base_url"])
|
|
@@ -158,7 +171,7 @@ class FalFileRepositoryBase(FileRepository):
|
|
|
158
171
|
headers=headers,
|
|
159
172
|
method="POST",
|
|
160
173
|
)
|
|
161
|
-
with
|
|
174
|
+
with _urlopen(req) as response:
|
|
162
175
|
result = json.load(response)
|
|
163
176
|
|
|
164
177
|
upload_url = result["upload_url"]
|
|
@@ -175,7 +188,7 @@ class FalFileRepositoryBase(FileRepository):
|
|
|
175
188
|
headers={"Content-Type": file.content_type},
|
|
176
189
|
)
|
|
177
190
|
|
|
178
|
-
with
|
|
191
|
+
with _urlopen(req, timeout=PUT_REQUEST_TIMEOUT):
|
|
179
192
|
pass
|
|
180
193
|
|
|
181
194
|
return result["file_url"]
|
|
@@ -252,7 +265,7 @@ class MultipartUploadGCS:
|
|
|
252
265
|
).encode(),
|
|
253
266
|
)
|
|
254
267
|
|
|
255
|
-
with
|
|
268
|
+
with _urlopen(req) as response:
|
|
256
269
|
result = json.load(response)
|
|
257
270
|
self._access_url = result["file_url"]
|
|
258
271
|
self._upload_url = result["upload_url"]
|
|
@@ -272,7 +285,7 @@ class MultipartUploadGCS:
|
|
|
272
285
|
)
|
|
273
286
|
|
|
274
287
|
try:
|
|
275
|
-
with
|
|
288
|
+
with _urlopen(req) as response:
|
|
276
289
|
result = json.load(response)
|
|
277
290
|
upload_url = result["upload_url"]
|
|
278
291
|
except HTTPError as exc:
|
|
@@ -288,7 +301,7 @@ class MultipartUploadGCS:
|
|
|
288
301
|
)
|
|
289
302
|
|
|
290
303
|
try:
|
|
291
|
-
with
|
|
304
|
+
with _urlopen(req, timeout=PUT_REQUEST_TIMEOUT) as resp:
|
|
292
305
|
self._parts.append(
|
|
293
306
|
{
|
|
294
307
|
"part_number": part_number,
|
|
@@ -318,7 +331,7 @@ class MultipartUploadGCS:
|
|
|
318
331
|
}
|
|
319
332
|
).encode(),
|
|
320
333
|
)
|
|
321
|
-
with
|
|
334
|
+
with _urlopen(req):
|
|
322
335
|
pass
|
|
323
336
|
except HTTPError as e:
|
|
324
337
|
raise FileUploadException(
|
|
@@ -523,7 +536,7 @@ class MultipartUpload:
|
|
|
523
536
|
}
|
|
524
537
|
).encode(),
|
|
525
538
|
)
|
|
526
|
-
with
|
|
539
|
+
with _urlopen(req) as response:
|
|
527
540
|
result = json.load(response)
|
|
528
541
|
self._upload_url = result["upload_url"]
|
|
529
542
|
self._file_url = result["file_url"]
|
|
@@ -543,7 +556,7 @@ class MultipartUpload:
|
|
|
543
556
|
)
|
|
544
557
|
|
|
545
558
|
try:
|
|
546
|
-
with
|
|
559
|
+
with _urlopen(req, timeout=PUT_REQUEST_TIMEOUT) as resp:
|
|
547
560
|
self._parts.append(
|
|
548
561
|
{
|
|
549
562
|
"part_number": part_number,
|
|
@@ -568,7 +581,7 @@ class MultipartUpload:
|
|
|
568
581
|
},
|
|
569
582
|
data=json.dumps({"parts": self._parts}).encode(),
|
|
570
583
|
)
|
|
571
|
-
with
|
|
584
|
+
with _urlopen(req):
|
|
572
585
|
pass
|
|
573
586
|
except HTTPError as e:
|
|
574
587
|
raise FileUploadException(
|
|
@@ -721,7 +734,7 @@ class MultipartUploadV3:
|
|
|
721
734
|
).encode(),
|
|
722
735
|
)
|
|
723
736
|
|
|
724
|
-
with
|
|
737
|
+
with _urlopen(req) as response:
|
|
725
738
|
result = json.load(response)
|
|
726
739
|
self._access_url = result["file_url"]
|
|
727
740
|
self._upload_url = result["upload_url"]
|
|
@@ -747,7 +760,7 @@ class MultipartUploadV3:
|
|
|
747
760
|
)
|
|
748
761
|
|
|
749
762
|
try:
|
|
750
|
-
with
|
|
763
|
+
with _urlopen(req, timeout=PUT_REQUEST_TIMEOUT) as resp:
|
|
751
764
|
self._parts.append(
|
|
752
765
|
{
|
|
753
766
|
"partNumber": part_number,
|
|
@@ -775,7 +788,7 @@ class MultipartUploadV3:
|
|
|
775
788
|
},
|
|
776
789
|
data=json.dumps({"parts": self._parts}).encode(),
|
|
777
790
|
)
|
|
778
|
-
with
|
|
791
|
+
with _urlopen(req):
|
|
779
792
|
pass
|
|
780
793
|
except HTTPError as e:
|
|
781
794
|
raise FileUploadException(
|
|
@@ -915,7 +928,7 @@ class InternalMultipartUploadV3:
|
|
|
915
928
|
"X-Fal-File-Name": self.file_name,
|
|
916
929
|
},
|
|
917
930
|
)
|
|
918
|
-
with
|
|
931
|
+
with _urlopen(req) as response:
|
|
919
932
|
result = json.load(response)
|
|
920
933
|
self._access_url = result["access_url"]
|
|
921
934
|
self._upload_id = result["uploadId"]
|
|
@@ -940,7 +953,7 @@ class InternalMultipartUploadV3:
|
|
|
940
953
|
)
|
|
941
954
|
|
|
942
955
|
try:
|
|
943
|
-
with
|
|
956
|
+
with _urlopen(req, timeout=PUT_REQUEST_TIMEOUT) as resp:
|
|
944
957
|
self._parts.append(
|
|
945
958
|
{
|
|
946
959
|
"partNumber": part_number,
|
|
@@ -966,7 +979,7 @@ class InternalMultipartUploadV3:
|
|
|
966
979
|
},
|
|
967
980
|
data=json.dumps({"parts": self._parts}).encode(),
|
|
968
981
|
)
|
|
969
|
-
with
|
|
982
|
+
with _urlopen(req):
|
|
970
983
|
pass
|
|
971
984
|
except HTTPError as e:
|
|
972
985
|
raise FileUploadException(
|
|
@@ -1092,7 +1105,7 @@ class FalFileRepositoryV2(FalFileRepositoryBase):
|
|
|
1092
1105
|
headers=headers,
|
|
1093
1106
|
method="PUT",
|
|
1094
1107
|
)
|
|
1095
|
-
with
|
|
1108
|
+
with _urlopen(req, timeout=PUT_REQUEST_TIMEOUT) as response:
|
|
1096
1109
|
result = json.load(response)
|
|
1097
1110
|
|
|
1098
1111
|
return result["file_url"]
|
|
@@ -1186,7 +1199,7 @@ class FalCDNFileRepository(FileRepository):
|
|
|
1186
1199
|
url = os.getenv("FAL_CDN_HOST", _FAL_CDN) + "/files/upload"
|
|
1187
1200
|
request = Request(url, headers=headers, method="POST", data=file.data)
|
|
1188
1201
|
try:
|
|
1189
|
-
with
|
|
1202
|
+
with _urlopen(request) as response:
|
|
1190
1203
|
result = json.load(response)
|
|
1191
1204
|
except HTTPError as e:
|
|
1192
1205
|
raise FileUploadException(
|
|
@@ -1266,7 +1279,7 @@ class FalFileRepositoryV3(FileRepository):
|
|
|
1266
1279
|
).encode(),
|
|
1267
1280
|
)
|
|
1268
1281
|
try:
|
|
1269
|
-
with
|
|
1282
|
+
with _urlopen(request) as response:
|
|
1270
1283
|
result = json.load(response)
|
|
1271
1284
|
file_url = result["file_url"]
|
|
1272
1285
|
upload_url = result["upload_url"]
|
|
@@ -1282,7 +1295,7 @@ class FalFileRepositoryV3(FileRepository):
|
|
|
1282
1295
|
data=file.data,
|
|
1283
1296
|
)
|
|
1284
1297
|
try:
|
|
1285
|
-
with
|
|
1298
|
+
with _urlopen(request, timeout=PUT_REQUEST_TIMEOUT):
|
|
1286
1299
|
pass
|
|
1287
1300
|
except HTTPError as e:
|
|
1288
1301
|
raise FileUploadException(
|
|
@@ -1380,7 +1393,7 @@ class InternalFalFileRepositoryV3(FileRepository):
|
|
|
1380
1393
|
url = os.getenv("FAL_CDN_V3_HOST", _FAL_CDN_V3) + "/files/upload"
|
|
1381
1394
|
request = Request(url, headers=headers, method="POST", data=file.data)
|
|
1382
1395
|
try:
|
|
1383
|
-
with
|
|
1396
|
+
with _urlopen(request) as response:
|
|
1384
1397
|
result = json.load(response)
|
|
1385
1398
|
except HTTPError as e:
|
|
1386
1399
|
raise FileUploadException(
|
|
@@ -504,6 +504,9 @@ def test_app_cancellation(test_app: str, test_cancellable_app: str):
|
|
|
504
504
|
assert response == {"result": 3}
|
|
505
505
|
|
|
506
506
|
|
|
507
|
+
@pytest.mark.xfail(
|
|
508
|
+
reason="Temporary disabled while investigating backend issue. Ping @efiop"
|
|
509
|
+
)
|
|
507
510
|
@pytest.mark.flaky(max_runs=3)
|
|
508
511
|
def test_app_client_async(test_sleep_app: str):
|
|
509
512
|
handle = apps.submit(test_sleep_app, arguments={"wait_time": 1})
|
fal-1.14.0/src/fal/files.py
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
from functools import lru_cache
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
from typing import Any, Dict, Optional, Sequence, Tuple, Union
|
|
4
|
-
|
|
5
|
-
import tomli
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@lru_cache
|
|
9
|
-
def _load_toml(path: Union[Path, str]) -> Dict[str, Any]:
|
|
10
|
-
with open(path, "rb") as f:
|
|
11
|
-
return tomli.load(f)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@lru_cache
|
|
15
|
-
def _cached_resolve(path: Path) -> Path:
|
|
16
|
-
return path.resolve()
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@lru_cache
|
|
20
|
-
def find_project_root(srcs: Optional[Sequence[str]]) -> Tuple[Path, str]:
|
|
21
|
-
"""Return a directory containing .git, or pyproject.toml.
|
|
22
|
-
|
|
23
|
-
That directory will be a common parent of all files and directories
|
|
24
|
-
passed in `srcs`.
|
|
25
|
-
|
|
26
|
-
If no directory in the tree contains a marker that would specify it's the
|
|
27
|
-
project root, the root of the file system is returned.
|
|
28
|
-
|
|
29
|
-
Returns a two-tuple with the first element as the project root path and
|
|
30
|
-
the second element as a string describing the method by which the
|
|
31
|
-
project root was discovered.
|
|
32
|
-
"""
|
|
33
|
-
if not srcs:
|
|
34
|
-
srcs = [str(_cached_resolve(Path.cwd()))]
|
|
35
|
-
|
|
36
|
-
path_srcs = [_cached_resolve(Path(Path.cwd(), src)) for src in srcs]
|
|
37
|
-
|
|
38
|
-
# A list of lists of parents for each 'src'. 'src' is included as a
|
|
39
|
-
# "parent" of itself if it is a directory
|
|
40
|
-
src_parents = [
|
|
41
|
-
list(path.parents) + ([path] if path.is_dir() else []) for path in path_srcs
|
|
42
|
-
]
|
|
43
|
-
|
|
44
|
-
common_base = max(
|
|
45
|
-
set.intersection(*(set(parents) for parents in src_parents)),
|
|
46
|
-
key=lambda path: path.parts,
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
for directory in (common_base, *common_base.parents):
|
|
50
|
-
if (directory / ".git").exists():
|
|
51
|
-
return directory, ".git directory"
|
|
52
|
-
|
|
53
|
-
if (directory / "pyproject.toml").is_file():
|
|
54
|
-
pyproject_toml = _load_toml(directory / "pyproject.toml")
|
|
55
|
-
if "fal" in pyproject_toml.get("tool", {}):
|
|
56
|
-
return directory, "pyproject.toml"
|
|
57
|
-
|
|
58
|
-
return directory, "file system root"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def find_pyproject_toml(
|
|
62
|
-
path_search_start: Optional[Tuple[str, ...]] = None,
|
|
63
|
-
) -> Optional[str]:
|
|
64
|
-
"""Find the absolute filepath to a pyproject.toml if it exists"""
|
|
65
|
-
path_project_root, _ = find_project_root(path_search_start)
|
|
66
|
-
path_pyproject_toml = path_project_root / "pyproject.toml"
|
|
67
|
-
|
|
68
|
-
if path_pyproject_toml.is_file():
|
|
69
|
-
return str(path_pyproject_toml)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def parse_pyproject_toml(path_config: str) -> Dict[str, Any]:
|
|
73
|
-
"""Parse a pyproject toml file, pulling out relevant parts for fal.
|
|
74
|
-
|
|
75
|
-
If parsing fails, will raise a tomli.TOMLDecodeError.
|
|
76
|
-
"""
|
|
77
|
-
pyproject_toml = _load_toml(path_config)
|
|
78
|
-
config: Dict[str, Any] = pyproject_toml.get("tool", {}).get("fal", {})
|
|
79
|
-
config = {k.replace("--", "").replace("-", "_"): v for k, v in config.items()}
|
|
80
|
-
|
|
81
|
-
return config
|
|
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
|
|
File without changes
|
{fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py
RENAMED
|
File without changes
|
{fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py
RENAMED
|
File without changes
|
|
File without changes
|
{fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py
RENAMED
|
File without changes
|
{fal-1.14.0 → fal-1.15.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|