fal 1.5.1__tar.gz → 1.5.3__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.5.1 → fal-1.5.3}/.gitignore +1 -0
- fal-1.5.3/Makefile +24 -0
- {fal-1.5.1 → fal-1.5.3}/PKG-INFO +6 -2
- fal-1.5.3/docs/conf.py +31 -0
- fal-1.5.3/docs/index.rst +18 -0
- {fal-1.5.1 → fal-1.5.3}/fal.egg-info/PKG-INFO +6 -2
- {fal-1.5.1 → fal-1.5.3}/fal.egg-info/SOURCES.txt +4 -0
- {fal-1.5.1 → fal-1.5.3}/fal.egg-info/requires.txt +6 -1
- {fal-1.5.1 → fal-1.5.3}/pyproject.toml +6 -1
- {fal-1.5.1 → fal-1.5.3}/src/fal/_fal_version.py +2 -2
- {fal-1.5.1 → fal-1.5.3}/src/fal/app.py +14 -7
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/file/file.py +33 -2
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/file/providers/fal.py +60 -5
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/file/types.py +11 -4
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/image/image.py +5 -0
- fal-1.5.3/tests/assets/cat.png +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/integration_test.py +6 -7
- {fal-1.5.1 → fal-1.5.3}/tests/test_stability.py +1 -0
- {fal-1.5.1 → fal-1.5.3}/README.md +0 -0
- {fal-1.5.1 → fal-1.5.3}/fal.egg-info/dependency_links.txt +0 -0
- {fal-1.5.1 → fal-1.5.3}/fal.egg-info/entry_points.txt +0 -0
- {fal-1.5.1 → fal-1.5.3}/fal.egg-info/top_level.txt +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/README.md +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi-fal-rest/pyproject.toml +0 -0
- {fal-1.5.1 → fal-1.5.3}/openapi_rest.config.yaml +0 -0
- {fal-1.5.1 → fal-1.5.3}/setup.cfg +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/__main__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/_serialization.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/_version.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/api.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/apps.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/auth/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/auth/auth0.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/auth/local.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/cli/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/cli/_utils.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/cli/apps.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/cli/auth.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/cli/create.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/cli/debug.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/cli/deploy.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/cli/doctor.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/cli/keys.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/cli/main.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/cli/parser.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/cli/run.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/cli/secrets.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/console/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/console/icons.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/console/ux.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/container.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/exceptions/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/exceptions/_base.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/exceptions/_cuda.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/exceptions/auth.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/files.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/flags.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/logging/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/logging/isolate.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/logging/style.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/logging/trace.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/logging/user.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/py.typed +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/rest_client.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/sdk.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/sync.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/exceptions.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/file/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/file/providers/gcp.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/file/providers/r2.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/image/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/image/safety_checker.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/optimize.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/utils/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/utils/download_utils.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/toolkit/utils/retry.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/utils.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/src/fal/workflows.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/cli/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/cli/test_apps.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/cli/test_auth.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/cli/test_deploy.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/cli/test_keys.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/cli/test_run.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/cli/test_secrets.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/conftest.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/mainify_package/__init__.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/mainify_package/impl.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/mainify_package/utils.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/mainify_target.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/test_apps.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/toolkit/file_test.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/toolkit/image_test.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tests/toolkit/utils/retry.py +0 -0
- {fal-1.5.1 → fal-1.5.3}/tools/demo_script.py +0 -0
fal-1.5.3/Makefile
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
MAKEFILE_DIR := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
|
|
2
|
+
|
|
3
|
+
SPHINXOPTS = --fresh-env
|
|
4
|
+
SPHINXBUILD = sphinx-build
|
|
5
|
+
SPHINXAPIDOC = sphinx-apidoc
|
|
6
|
+
SOURCEDIR = $(MAKEFILE_DIR)/docs
|
|
7
|
+
BUILDDIR = $(MAKEFILE_DIR)/docs/_build
|
|
8
|
+
PROJECTDIR = $(MAKEFILE_DIR)/src/fal
|
|
9
|
+
|
|
10
|
+
help:
|
|
11
|
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
12
|
+
|
|
13
|
+
.PHONY: help Makefile clean apidoc html docs
|
|
14
|
+
|
|
15
|
+
clean:
|
|
16
|
+
rm -rf $(BUILDDIR)/*
|
|
17
|
+
|
|
18
|
+
apidoc:
|
|
19
|
+
$(SPHINXAPIDOC) -f -o "$(SOURCEDIR)" "$(PROJECTDIR)"
|
|
20
|
+
|
|
21
|
+
html:
|
|
22
|
+
@$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
23
|
+
|
|
24
|
+
docs: apidoc html
|
{fal-1.5.1 → fal-1.5.3}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fal
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.3
|
|
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
|
|
@@ -37,6 +37,10 @@ Requires-Dist: pyjwt[crypto]<3,>=2.8.0
|
|
|
37
37
|
Requires-Dist: uvicorn<1,>=0.29.0
|
|
38
38
|
Requires-Dist: cookiecutter
|
|
39
39
|
Requires-Dist: tomli
|
|
40
|
+
Provides-Extra: docs
|
|
41
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
42
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
43
|
+
Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
|
|
40
44
|
Provides-Extra: test
|
|
41
45
|
Requires-Dist: pytest<8; extra == "test"
|
|
42
46
|
Requires-Dist: pytest-asyncio; extra == "test"
|
|
@@ -44,7 +48,7 @@ Requires-Dist: pytest-xdist; extra == "test"
|
|
|
44
48
|
Requires-Dist: flaky; extra == "test"
|
|
45
49
|
Requires-Dist: boto3; extra == "test"
|
|
46
50
|
Provides-Extra: dev
|
|
47
|
-
Requires-Dist: fal[test]; extra == "dev"
|
|
51
|
+
Requires-Dist: fal[docs,test]; extra == "dev"
|
|
48
52
|
Requires-Dist: openapi-python-client<1,>=0.14.1; extra == "dev"
|
|
49
53
|
|
|
50
54
|
[](https://pypi.org/project/fal)
|
fal-1.5.3/docs/conf.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Configuration file for the Sphinx documentation builder.
|
|
2
|
+
#
|
|
3
|
+
# For the full list of built-in configuration values, see the documentation:
|
|
4
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
|
5
|
+
|
|
6
|
+
# -- Project information -----------------------------------------------------
|
|
7
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
|
8
|
+
|
|
9
|
+
project = 'fal_sdk'
|
|
10
|
+
copyright = '2024, FAL Team'
|
|
11
|
+
author = 'FAL Team'
|
|
12
|
+
|
|
13
|
+
# -- General configuration ---------------------------------------------------
|
|
14
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
|
15
|
+
|
|
16
|
+
extensions = [
|
|
17
|
+
'sphinx.ext.autodoc',
|
|
18
|
+
'sphinx.ext.napoleon',
|
|
19
|
+
'sphinx_autodoc_typehints'
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
templates_path = ['_templates']
|
|
23
|
+
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# -- Options for HTML output -------------------------------------------------
|
|
28
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
|
29
|
+
|
|
30
|
+
html_theme = 'alabaster'
|
|
31
|
+
html_static_path = ['_static']
|
fal-1.5.3/docs/index.rst
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.. fal_sdk documentation master file, created by
|
|
2
|
+
sphinx-quickstart on Wed Oct 2 05:09:49 2024.
|
|
3
|
+
You can adapt this file completely to your liking, but it should at least
|
|
4
|
+
contain the root `toctree` directive.
|
|
5
|
+
|
|
6
|
+
fal_sdk documentation
|
|
7
|
+
=====================
|
|
8
|
+
|
|
9
|
+
Add your content using ``reStructuredText`` syntax. See the
|
|
10
|
+
`reStructuredText <https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html>`_
|
|
11
|
+
documentation for details.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
.. toctree::
|
|
15
|
+
:maxdepth: 2
|
|
16
|
+
:caption: Contents:
|
|
17
|
+
|
|
18
|
+
modules
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fal
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.3
|
|
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
|
|
@@ -37,6 +37,10 @@ Requires-Dist: pyjwt[crypto]<3,>=2.8.0
|
|
|
37
37
|
Requires-Dist: uvicorn<1,>=0.29.0
|
|
38
38
|
Requires-Dist: cookiecutter
|
|
39
39
|
Requires-Dist: tomli
|
|
40
|
+
Provides-Extra: docs
|
|
41
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
42
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
43
|
+
Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
|
|
40
44
|
Provides-Extra: test
|
|
41
45
|
Requires-Dist: pytest<8; extra == "test"
|
|
42
46
|
Requires-Dist: pytest-asyncio; extra == "test"
|
|
@@ -44,7 +48,7 @@ Requires-Dist: pytest-xdist; extra == "test"
|
|
|
44
48
|
Requires-Dist: flaky; extra == "test"
|
|
45
49
|
Requires-Dist: boto3; extra == "test"
|
|
46
50
|
Provides-Extra: dev
|
|
47
|
-
Requires-Dist: fal[test]; extra == "dev"
|
|
51
|
+
Requires-Dist: fal[docs,test]; extra == "dev"
|
|
48
52
|
Requires-Dist: openapi-python-client<1,>=0.14.1; extra == "dev"
|
|
49
53
|
|
|
50
54
|
[](https://pypi.org/project/fal)
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
.gitignore
|
|
2
|
+
Makefile
|
|
2
3
|
README.md
|
|
3
4
|
openapi_rest.config.yaml
|
|
4
5
|
pyproject.toml
|
|
6
|
+
docs/conf.py
|
|
7
|
+
docs/index.rst
|
|
5
8
|
fal.egg-info/PKG-INFO
|
|
6
9
|
fal.egg-info/SOURCES.txt
|
|
7
10
|
fal.egg-info/dependency_links.txt
|
|
@@ -144,6 +147,7 @@ tests/integration_test.py
|
|
|
144
147
|
tests/mainify_target.py
|
|
145
148
|
tests/test_apps.py
|
|
146
149
|
tests/test_stability.py
|
|
150
|
+
tests/assets/cat.png
|
|
147
151
|
tests/cli/__init__.py
|
|
148
152
|
tests/cli/test_apps.py
|
|
149
153
|
tests/cli/test_auth.py
|
|
@@ -60,6 +60,11 @@ dependencies = [
|
|
|
60
60
|
]
|
|
61
61
|
|
|
62
62
|
[project.optional-dependencies]
|
|
63
|
+
docs = [
|
|
64
|
+
"sphinx",
|
|
65
|
+
"sphinx-rtd-theme",
|
|
66
|
+
"sphinx-autodoc-typehints",
|
|
67
|
+
]
|
|
63
68
|
test = [
|
|
64
69
|
"pytest<8",
|
|
65
70
|
"pytest-asyncio",
|
|
@@ -68,7 +73,7 @@ test = [
|
|
|
68
73
|
"boto3",
|
|
69
74
|
]
|
|
70
75
|
dev = [
|
|
71
|
-
"fal[test]",
|
|
76
|
+
"fal[docs,test]",
|
|
72
77
|
"openapi-python-client>=0.14.1,<1",
|
|
73
78
|
]
|
|
74
79
|
|
|
@@ -19,7 +19,8 @@ from fal._serialization import include_modules_from
|
|
|
19
19
|
from fal.api import RouteSignature
|
|
20
20
|
from fal.exceptions import RequestCancelledException
|
|
21
21
|
from fal.logging import get_logger
|
|
22
|
-
from fal.toolkit.file
|
|
22
|
+
from fal.toolkit.file import get_lifecycle_preference
|
|
23
|
+
from fal.toolkit.file.providers.fal import GLOBAL_LIFECYCLE_PREFERENCE
|
|
23
24
|
|
|
24
25
|
REALTIME_APP_REQUIREMENTS = ["websockets", "msgpack"]
|
|
25
26
|
|
|
@@ -266,11 +267,14 @@ class App(fal.api.BaseServable):
|
|
|
266
267
|
|
|
267
268
|
@app.middleware("http")
|
|
268
269
|
async def set_global_object_preference(request, call_next):
|
|
269
|
-
response = await call_next(request)
|
|
270
270
|
try:
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
271
|
+
preference_dict = get_lifecycle_preference(request) or {}
|
|
272
|
+
expiration_duration = preference_dict.get("expiration_duration_seconds")
|
|
273
|
+
if expiration_duration is not None:
|
|
274
|
+
GLOBAL_LIFECYCLE_PREFERENCE.expiration_duration_seconds = int(
|
|
275
|
+
expiration_duration
|
|
276
|
+
)
|
|
277
|
+
|
|
274
278
|
except Exception:
|
|
275
279
|
from fastapi.logger import logger
|
|
276
280
|
|
|
@@ -278,7 +282,7 @@ class App(fal.api.BaseServable):
|
|
|
278
282
|
"Failed set a global lifecycle preference %s",
|
|
279
283
|
self.__class__.__name__,
|
|
280
284
|
)
|
|
281
|
-
return
|
|
285
|
+
return await call_next(request)
|
|
282
286
|
|
|
283
287
|
@app.exception_handler(RequestCancelledException)
|
|
284
288
|
async def value_error_exception_handler(
|
|
@@ -402,7 +406,10 @@ def _fal_websocket_template(
|
|
|
402
406
|
batch.append(next_input)
|
|
403
407
|
|
|
404
408
|
t0 = loop.time()
|
|
405
|
-
|
|
409
|
+
if inspect.iscoroutinefunction(func):
|
|
410
|
+
output = await func(self, *batch)
|
|
411
|
+
else:
|
|
412
|
+
output = await loop.run_in_executor(None, func, self, *batch) # type: ignore
|
|
406
413
|
total_time = loop.time() - t0
|
|
407
414
|
if not isinstance(output, dict):
|
|
408
415
|
# Handle pydantic output modal
|
|
@@ -8,6 +8,7 @@ from urllib.parse import urlparse
|
|
|
8
8
|
from zipfile import ZipFile
|
|
9
9
|
|
|
10
10
|
import pydantic
|
|
11
|
+
from fastapi import Request
|
|
11
12
|
|
|
12
13
|
# https://github.com/pydantic/pydantic/pull/2573
|
|
13
14
|
if not hasattr(pydantic, "__version__") or pydantic.__version__.startswith("1."):
|
|
@@ -24,6 +25,7 @@ from fal.toolkit.file.providers.fal import (
|
|
|
24
25
|
FalCDNFileRepository,
|
|
25
26
|
FalFileRepository,
|
|
26
27
|
FalFileRepositoryV2,
|
|
28
|
+
FalFileRepositoryV3,
|
|
27
29
|
InMemoryRepository,
|
|
28
30
|
)
|
|
29
31
|
from fal.toolkit.file.providers.gcp import GoogleStorageRepository
|
|
@@ -36,6 +38,7 @@ FileRepositoryFactory = Callable[[], FileRepository]
|
|
|
36
38
|
BUILT_IN_REPOSITORIES: dict[RepositoryId, FileRepositoryFactory] = {
|
|
37
39
|
"fal": lambda: FalFileRepository(),
|
|
38
40
|
"fal_v2": lambda: FalFileRepositoryV2(),
|
|
41
|
+
"fal_v3": lambda: FalFileRepositoryV3(),
|
|
39
42
|
"in_memory": lambda: InMemoryRepository(),
|
|
40
43
|
"gcp_storage": lambda: GoogleStorageRepository(),
|
|
41
44
|
"r2": lambda: R2Repository(),
|
|
@@ -53,6 +56,7 @@ get_builtin_repository.__module__ = "__main__"
|
|
|
53
56
|
|
|
54
57
|
DEFAULT_REPOSITORY: FileRepository | RepositoryId = "fal_v2"
|
|
55
58
|
FALLBACK_REPOSITORY: FileRepository | RepositoryId = "cdn"
|
|
59
|
+
OBJECT_LIFECYCLE_PREFERENCE_KEY = "x-fal-object-lifecycle-preference"
|
|
56
60
|
|
|
57
61
|
|
|
58
62
|
class File(BaseModel):
|
|
@@ -130,6 +134,7 @@ class File(BaseModel):
|
|
|
130
134
|
fallback_repository: Optional[
|
|
131
135
|
FileRepository | RepositoryId
|
|
132
136
|
] = FALLBACK_REPOSITORY,
|
|
137
|
+
request: Optional[Request] = None,
|
|
133
138
|
) -> File:
|
|
134
139
|
repo = (
|
|
135
140
|
repository
|
|
@@ -139,8 +144,10 @@ class File(BaseModel):
|
|
|
139
144
|
|
|
140
145
|
fdata = FileData(data, content_type, file_name)
|
|
141
146
|
|
|
147
|
+
object_lifecycle_preference = get_lifecycle_preference(request)
|
|
148
|
+
|
|
142
149
|
try:
|
|
143
|
-
url = repo.save(fdata)
|
|
150
|
+
url = repo.save(fdata, object_lifecycle_preference)
|
|
144
151
|
except Exception:
|
|
145
152
|
if not fallback_repository:
|
|
146
153
|
raise
|
|
@@ -151,7 +158,7 @@ class File(BaseModel):
|
|
|
151
158
|
else get_builtin_repository(fallback_repository)
|
|
152
159
|
)
|
|
153
160
|
|
|
154
|
-
url = fallback_repo.save(fdata)
|
|
161
|
+
url = fallback_repo.save(fdata, object_lifecycle_preference)
|
|
155
162
|
|
|
156
163
|
return cls(
|
|
157
164
|
url=url,
|
|
@@ -171,6 +178,7 @@ class File(BaseModel):
|
|
|
171
178
|
fallback_repository: Optional[
|
|
172
179
|
FileRepository | RepositoryId
|
|
173
180
|
] = FALLBACK_REPOSITORY,
|
|
181
|
+
request: Optional[Request] = None,
|
|
174
182
|
) -> File:
|
|
175
183
|
file_path = Path(path)
|
|
176
184
|
if not file_path.exists():
|
|
@@ -183,12 +191,14 @@ class File(BaseModel):
|
|
|
183
191
|
)
|
|
184
192
|
|
|
185
193
|
content_type = content_type or "application/octet-stream"
|
|
194
|
+
object_lifecycle_preference = get_lifecycle_preference(request)
|
|
186
195
|
|
|
187
196
|
try:
|
|
188
197
|
url, data = repo.save_file(
|
|
189
198
|
file_path,
|
|
190
199
|
content_type=content_type,
|
|
191
200
|
multipart=multipart,
|
|
201
|
+
object_lifecycle_preference=object_lifecycle_preference,
|
|
192
202
|
)
|
|
193
203
|
except Exception:
|
|
194
204
|
if not fallback_repository:
|
|
@@ -204,6 +214,7 @@ class File(BaseModel):
|
|
|
204
214
|
file_path,
|
|
205
215
|
content_type=content_type,
|
|
206
216
|
multipart=multipart,
|
|
217
|
+
object_lifecycle_preference=object_lifecycle_preference,
|
|
207
218
|
)
|
|
208
219
|
|
|
209
220
|
return cls(
|
|
@@ -261,3 +272,23 @@ class CompressedFile(File):
|
|
|
261
272
|
def __del__(self):
|
|
262
273
|
if self.extract_dir:
|
|
263
274
|
shutil.rmtree(self.extract_dir)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def get_lifecycle_preference(request: Request) -> dict[str, str] | None:
|
|
278
|
+
import json
|
|
279
|
+
|
|
280
|
+
preference_str = (
|
|
281
|
+
request.headers.get(OBJECT_LIFECYCLE_PREFERENCE_KEY)
|
|
282
|
+
if request is not None
|
|
283
|
+
else None
|
|
284
|
+
)
|
|
285
|
+
if preference_str is None:
|
|
286
|
+
return None
|
|
287
|
+
|
|
288
|
+
object_lifecycle_preference = {}
|
|
289
|
+
try:
|
|
290
|
+
object_lifecycle_preference = json.loads(preference_str)
|
|
291
|
+
return object_lifecycle_preference
|
|
292
|
+
except Exception as e:
|
|
293
|
+
print(f"Failed to parse object lifecycle preference: {e}")
|
|
294
|
+
return None
|
|
@@ -19,6 +19,7 @@ from fal.toolkit.file.types import FileData, FileRepository
|
|
|
19
19
|
from fal.toolkit.utils.retry import retry
|
|
20
20
|
|
|
21
21
|
_FAL_CDN = "https://fal.media"
|
|
22
|
+
_FAL_CDN_V3 = "https://v3.fal.media"
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
@dataclass
|
|
@@ -91,11 +92,11 @@ fal_v2_token_manager = FalV2TokenManager()
|
|
|
91
92
|
|
|
92
93
|
@dataclass
|
|
93
94
|
class ObjectLifecyclePreference:
|
|
94
|
-
|
|
95
|
+
expiration_duration_seconds: int
|
|
95
96
|
|
|
96
97
|
|
|
97
98
|
GLOBAL_LIFECYCLE_PREFERENCE = ObjectLifecyclePreference(
|
|
98
|
-
|
|
99
|
+
expiration_duration_seconds=86400
|
|
99
100
|
)
|
|
100
101
|
|
|
101
102
|
|
|
@@ -158,7 +159,9 @@ class FalFileRepositoryBase(FileRepository):
|
|
|
158
159
|
|
|
159
160
|
@dataclass
|
|
160
161
|
class FalFileRepository(FalFileRepositoryBase):
|
|
161
|
-
def save(
|
|
162
|
+
def save(
|
|
163
|
+
self, file: FileData, object_lifecycle_preference: dict[str, str] | None = None
|
|
164
|
+
) -> str:
|
|
162
165
|
return self._save(file, "gcs")
|
|
163
166
|
|
|
164
167
|
|
|
@@ -275,7 +278,9 @@ class MultipartUpload:
|
|
|
275
278
|
@dataclass
|
|
276
279
|
class FalFileRepositoryV2(FalFileRepositoryBase):
|
|
277
280
|
@retry(max_retries=3, base_delay=1, backoff_type="exponential", jitter=True)
|
|
278
|
-
def save(
|
|
281
|
+
def save(
|
|
282
|
+
self, file: FileData, object_lifecycle_preference: dict[str, str] | None = None
|
|
283
|
+
) -> str:
|
|
279
284
|
token = fal_v2_token_manager.get_token()
|
|
280
285
|
headers = {
|
|
281
286
|
"Authorization": f"{token.token_type} {token.token}",
|
|
@@ -327,6 +332,7 @@ class FalFileRepositoryV2(FalFileRepositoryBase):
|
|
|
327
332
|
multipart_threshold: int | None = None,
|
|
328
333
|
multipart_chunk_size: int | None = None,
|
|
329
334
|
multipart_max_concurrency: int | None = None,
|
|
335
|
+
object_lifecycle_preference: dict[str, str] | None = None,
|
|
330
336
|
) -> tuple[str, FileData | None]:
|
|
331
337
|
if multipart is None:
|
|
332
338
|
threshold = multipart_threshold or MultipartUpload.MULTIPART_THRESHOLD
|
|
@@ -347,7 +353,7 @@ class FalFileRepositoryV2(FalFileRepositoryBase):
|
|
|
347
353
|
content_type=content_type,
|
|
348
354
|
file_name=os.path.basename(file_path),
|
|
349
355
|
)
|
|
350
|
-
url = self.save(data)
|
|
356
|
+
url = self.save(data, object_lifecycle_preference)
|
|
351
357
|
|
|
352
358
|
return url, data
|
|
353
359
|
|
|
@@ -357,6 +363,7 @@ class InMemoryRepository(FileRepository):
|
|
|
357
363
|
def save(
|
|
358
364
|
self,
|
|
359
365
|
file: FileData,
|
|
366
|
+
object_lifecycle_preference: dict[str, str] | None = None,
|
|
360
367
|
) -> str:
|
|
361
368
|
return f'data:{file.content_type};base64,{b64encode(file.data).decode("utf-8")}'
|
|
362
369
|
|
|
@@ -367,6 +374,7 @@ class FalCDNFileRepository(FileRepository):
|
|
|
367
374
|
def save(
|
|
368
375
|
self,
|
|
369
376
|
file: FileData,
|
|
377
|
+
object_lifecycle_preference: dict[str, str] | None = None,
|
|
370
378
|
) -> str:
|
|
371
379
|
headers = {
|
|
372
380
|
**self.auth_headers,
|
|
@@ -401,3 +409,50 @@ class FalCDNFileRepository(FileRepository):
|
|
|
401
409
|
"Authorization": f"Bearer {key_id}:{key_secret}",
|
|
402
410
|
"User-Agent": "fal/0.1.0",
|
|
403
411
|
}
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
@dataclass
|
|
415
|
+
class FalFileRepositoryV3(FileRepository):
|
|
416
|
+
@retry(max_retries=3, base_delay=1, backoff_type="exponential", jitter=True)
|
|
417
|
+
def save(
|
|
418
|
+
self, file: FileData, user_lifecycle_preference: dict[str, str] | None
|
|
419
|
+
) -> str:
|
|
420
|
+
object_lifecycle_preference = dataclasses.asdict(GLOBAL_LIFECYCLE_PREFERENCE)
|
|
421
|
+
|
|
422
|
+
if user_lifecycle_preference is not None:
|
|
423
|
+
object_lifecycle_preference = {
|
|
424
|
+
key: user_lifecycle_preference[key]
|
|
425
|
+
if key in user_lifecycle_preference
|
|
426
|
+
else value
|
|
427
|
+
for key, value in object_lifecycle_preference.items()
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
headers = {
|
|
431
|
+
**self.auth_headers,
|
|
432
|
+
"Accept": "application/json",
|
|
433
|
+
"Content-Type": file.content_type,
|
|
434
|
+
"X-Fal-File-Name": file.file_name,
|
|
435
|
+
"X-Fal-Object-Lifecycle-Preference": json.dumps(
|
|
436
|
+
object_lifecycle_preference
|
|
437
|
+
),
|
|
438
|
+
}
|
|
439
|
+
url = os.getenv("FAL_CDN_V3_HOST", _FAL_CDN_V3) + "/files/upload"
|
|
440
|
+
request = Request(url, headers=headers, method="POST", data=file.data)
|
|
441
|
+
try:
|
|
442
|
+
with urlopen(request) as response:
|
|
443
|
+
result = json.load(response)
|
|
444
|
+
except HTTPError as e:
|
|
445
|
+
raise FileUploadException(
|
|
446
|
+
f"Error initiating upload. Status {e.status}: {e.reason}"
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
access_url = result["access_url"]
|
|
450
|
+
return access_url
|
|
451
|
+
|
|
452
|
+
@property
|
|
453
|
+
def auth_headers(self) -> dict[str, str]:
|
|
454
|
+
token = fal_v2_token_manager.get_token()
|
|
455
|
+
return {
|
|
456
|
+
"Authorization": f"{token.token_type} {token.token}",
|
|
457
|
+
"User-Agent": "fal/0.1.0",
|
|
458
|
+
}
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from mimetypes import guess_extension, guess_type
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Literal
|
|
6
|
+
from typing import Literal, Optional
|
|
7
7
|
from uuid import uuid4
|
|
8
8
|
|
|
9
9
|
|
|
@@ -29,12 +29,18 @@ class FileData:
|
|
|
29
29
|
self.file_name = file_name
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
RepositoryId = Literal[
|
|
32
|
+
RepositoryId = Literal[
|
|
33
|
+
"fal", "fal_v2", "fal_v3", "in_memory", "gcp_storage", "r2", "cdn"
|
|
34
|
+
]
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
@dataclass
|
|
36
38
|
class FileRepository:
|
|
37
|
-
def save(
|
|
39
|
+
def save(
|
|
40
|
+
self,
|
|
41
|
+
data: FileData,
|
|
42
|
+
object_lifecycle_preference: Optional[dict[str, str]] = None,
|
|
43
|
+
) -> str:
|
|
38
44
|
raise NotImplementedError()
|
|
39
45
|
|
|
40
46
|
def save_file(
|
|
@@ -45,6 +51,7 @@ class FileRepository:
|
|
|
45
51
|
multipart_threshold: int | None = None,
|
|
46
52
|
multipart_chunk_size: int | None = None,
|
|
47
53
|
multipart_max_concurrency: int | None = None,
|
|
54
|
+
object_lifecycle_preference: Optional[dict[str, str]] = None,
|
|
48
55
|
) -> tuple[str, FileData | None]:
|
|
49
56
|
if multipart:
|
|
50
57
|
raise NotImplementedError()
|
|
@@ -52,4 +59,4 @@ class FileRepository:
|
|
|
52
59
|
with open(file_path, "rb") as fobj:
|
|
53
60
|
data = FileData(fobj.read(), content_type, Path(file_path).name)
|
|
54
61
|
|
|
55
|
-
return self.save(data), data
|
|
62
|
+
return self.save(data, object_lifecycle_preference), data
|
|
@@ -4,6 +4,7 @@ import io
|
|
|
4
4
|
from tempfile import NamedTemporaryFile
|
|
5
5
|
from typing import TYPE_CHECKING, Literal, Optional, Union
|
|
6
6
|
|
|
7
|
+
from fastapi import Request
|
|
7
8
|
from pydantic import BaseModel, Field
|
|
8
9
|
|
|
9
10
|
from fal.toolkit.file.file import DEFAULT_REPOSITORY, FALLBACK_REPOSITORY, File
|
|
@@ -82,6 +83,7 @@ class Image(File):
|
|
|
82
83
|
fallback_repository: Optional[
|
|
83
84
|
FileRepository | RepositoryId
|
|
84
85
|
] = FALLBACK_REPOSITORY,
|
|
86
|
+
request: Optional[Request] = None,
|
|
85
87
|
) -> Image:
|
|
86
88
|
obj = super().from_bytes(
|
|
87
89
|
data,
|
|
@@ -89,6 +91,7 @@ class Image(File):
|
|
|
89
91
|
file_name=file_name,
|
|
90
92
|
repository=repository,
|
|
91
93
|
fallback_repository=fallback_repository,
|
|
94
|
+
request=request,
|
|
92
95
|
)
|
|
93
96
|
obj.width = size.width if size else None
|
|
94
97
|
obj.height = size.height if size else None
|
|
@@ -104,6 +107,7 @@ class Image(File):
|
|
|
104
107
|
fallback_repository: Optional[
|
|
105
108
|
FileRepository | RepositoryId
|
|
106
109
|
] = FALLBACK_REPOSITORY,
|
|
110
|
+
request: Optional[Request] = None,
|
|
107
111
|
) -> Image:
|
|
108
112
|
size = ImageSize(width=pil_image.width, height=pil_image.height)
|
|
109
113
|
if format is None:
|
|
@@ -133,6 +137,7 @@ class Image(File):
|
|
|
133
137
|
file_name,
|
|
134
138
|
repository,
|
|
135
139
|
fallback_repository=fallback_repository,
|
|
140
|
+
request=request,
|
|
136
141
|
)
|
|
137
142
|
|
|
138
143
|
def to_pil(self, mode: str = "RGB") -> PILImage.Image:
|
|
Binary file
|
|
@@ -23,6 +23,8 @@ from fal.toolkit.utils.download_utils import _get_git_revision_hash, _hash_url
|
|
|
23
23
|
from pydantic import BaseModel, Field
|
|
24
24
|
from pydantic import __version__ as pydantic_version
|
|
25
25
|
|
|
26
|
+
EXAMPLE_FILE_URL = "https://raw.githubusercontent.com/fal-ai/fal/main/projects/fal/tests/assets/cat.png"
|
|
27
|
+
|
|
26
28
|
|
|
27
29
|
@pytest.mark.flaky(max_runs=3)
|
|
28
30
|
def test_isolated(isolated_client: Callable[..., Callable[..., IsolatedFunction]]):
|
|
@@ -197,11 +199,9 @@ def mock_fal_persistent_dirs(monkeypatch):
|
|
|
197
199
|
def test_download_file(isolated_client, mock_fal_persistent_dirs):
|
|
198
200
|
from fal.toolkit.utils.download_utils import FAL_PERSISTENT_DIR
|
|
199
201
|
|
|
200
|
-
EXAMPLE_FILE_URL = "https://raw.githubusercontent.com/fal-ai/isolate/d553f927348206530208442556f481f39b161732/README.md"
|
|
201
|
-
|
|
202
202
|
relative_directory = "test"
|
|
203
203
|
output_directory = FAL_PERSISTENT_DIR / relative_directory
|
|
204
|
-
expected_path = output_directory / "
|
|
204
|
+
expected_path = output_directory / "cat.png"
|
|
205
205
|
|
|
206
206
|
@isolated_client()
|
|
207
207
|
def absolute_path_persistent_dir():
|
|
@@ -301,8 +301,7 @@ def test_download_model_weights(isolated_client, mock_fal_persistent_dirs):
|
|
|
301
301
|
|
|
302
302
|
print(FAL_MODEL_WEIGHTS_DIR)
|
|
303
303
|
|
|
304
|
-
|
|
305
|
-
expected_path = FAL_MODEL_WEIGHTS_DIR / _hash_url(EXAMPLE_FILE_URL) / "README.md"
|
|
304
|
+
expected_path = FAL_MODEL_WEIGHTS_DIR / _hash_url(EXAMPLE_FILE_URL) / "cat.png"
|
|
306
305
|
|
|
307
306
|
@isolated_client()
|
|
308
307
|
def download_weights():
|
|
@@ -510,8 +509,8 @@ def test_fal_file_save(isolated_client):
|
|
|
510
509
|
"file_url, expected_content",
|
|
511
510
|
[
|
|
512
511
|
(
|
|
513
|
-
|
|
514
|
-
"projects/fal/
|
|
512
|
+
EXAMPLE_FILE_URL,
|
|
513
|
+
"projects/fal/cat.png",
|
|
515
514
|
),
|
|
516
515
|
("data:text/plain;charset=UTF-8,fal", "fal"),
|
|
517
516
|
],
|
|
@@ -580,6 +580,7 @@ def test_worker_env_vars(isolated_client):
|
|
|
580
580
|
"https://storage.googleapis.com/isolate-dev-smiling-shark_toolkit_bucket/",
|
|
581
581
|
),
|
|
582
582
|
("fal_v2", "https://v2.fal.media/files"),
|
|
583
|
+
("fal_v3", "https://v3.fal.media/files"),
|
|
583
584
|
],
|
|
584
585
|
)
|
|
585
586
|
def test_fal_storage(isolated_client, repo_type, url_prefix):
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|