fal 0.15.0__tar.gz → 1.0.0__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.

Files changed (133) hide show
  1. fal-1.0.0/.gitignore +1 -0
  2. {fal-0.15.0 → fal-1.0.0}/PKG-INFO +33 -5
  3. {fal-0.15.0 → fal-1.0.0}/README.md +27 -0
  4. {fal-0.15.0 → fal-1.0.0}/fal.egg-info/PKG-INFO +33 -5
  5. {fal-0.15.0 → fal-1.0.0}/fal.egg-info/SOURCES.txt +21 -2
  6. fal-1.0.0/fal.egg-info/entry_points.txt +2 -0
  7. {fal-0.15.0 → fal-1.0.0}/fal.egg-info/requires.txt +5 -4
  8. {fal-0.15.0 → fal-1.0.0}/pyproject.toml +16 -5
  9. fal-1.0.0/src/fal/__init__.py +29 -0
  10. fal-1.0.0/src/fal/__main__.py +4 -0
  11. fal-1.0.0/src/fal/_fal_version.py +16 -0
  12. {fal-0.15.0 → fal-1.0.0}/src/fal/_serialization.py +15 -9
  13. fal-1.0.0/src/fal/_version.py +6 -0
  14. {fal-0.15.0 → fal-1.0.0}/src/fal/api.py +32 -14
  15. {fal-0.15.0 → fal-1.0.0}/src/fal/app.py +54 -5
  16. {fal-0.15.0 → fal-1.0.0}/src/fal/auth/__init__.py +2 -1
  17. {fal-0.15.0 → fal-1.0.0}/src/fal/auth/auth0.py +4 -2
  18. {fal-0.15.0 → fal-1.0.0}/src/fal/auth/local.py +2 -1
  19. fal-1.0.0/src/fal/cli/__init__.py +1 -0
  20. fal-1.0.0/src/fal/cli/apps.py +313 -0
  21. fal-1.0.0/src/fal/cli/auth.py +59 -0
  22. fal-1.0.0/src/fal/cli/debug.py +65 -0
  23. fal-1.0.0/src/fal/cli/deploy.py +146 -0
  24. fal-1.0.0/src/fal/cli/keys.py +118 -0
  25. fal-1.0.0/src/fal/cli/main.py +82 -0
  26. fal-1.0.0/src/fal/cli/parser.py +74 -0
  27. fal-1.0.0/src/fal/cli/run.py +33 -0
  28. fal-1.0.0/src/fal/cli/secrets.py +107 -0
  29. fal-1.0.0/src/fal/exceptions/__init__.py +3 -0
  30. {fal-0.15.0 → fal-1.0.0}/src/fal/flags.py +0 -3
  31. {fal-0.15.0 → fal-1.0.0}/src/fal/logging/isolate.py +4 -4
  32. {fal-0.15.0 → fal-1.0.0}/src/fal/sdk.py +39 -2
  33. {fal-0.15.0 → fal-1.0.0}/src/fal/sync.py +7 -3
  34. {fal-0.15.0 → fal-1.0.0}/src/fal/toolkit/file/file.py +14 -6
  35. {fal-0.15.0 → fal-1.0.0}/src/fal/toolkit/file/providers/fal.py +20 -3
  36. {fal-0.15.0 → fal-1.0.0}/src/fal/toolkit/image/image.py +1 -1
  37. {fal-0.15.0 → fal-1.0.0}/src/fal/toolkit/optimize.py +0 -1
  38. {fal-0.15.0 → fal-1.0.0}/src/fal/toolkit/utils/download_utils.py +6 -3
  39. fal-1.0.0/src/fal/utils.py +55 -0
  40. {fal-0.15.0 → fal-1.0.0}/src/fal/workflows.py +7 -2
  41. fal-1.0.0/tests/cli/__init__.py +0 -0
  42. fal-1.0.0/tests/cli/test_apps.py +63 -0
  43. fal-1.0.0/tests/cli/test_auth.py +17 -0
  44. fal-1.0.0/tests/cli/test_deploy.py +8 -0
  45. fal-1.0.0/tests/cli/test_keys.py +27 -0
  46. fal-1.0.0/tests/cli/test_run.py +8 -0
  47. fal-1.0.0/tests/cli/test_secrets.py +19 -0
  48. {fal-0.15.0 → fal-1.0.0}/tests/conftest.py +0 -1
  49. {fal-0.15.0 → fal-1.0.0}/tests/integration_test.py +35 -16
  50. {fal-0.15.0 → fal-1.0.0}/tests/test_apps.py +26 -26
  51. {fal-0.15.0 → fal-1.0.0}/tests/test_stability.py +31 -7
  52. {fal-0.15.0 → fal-1.0.0}/tests/toolkit/file_test.py +2 -1
  53. {fal-0.15.0 → fal-1.0.0}/tests/toolkit/image_test.py +13 -4
  54. fal-0.15.0/fal.egg-info/entry_points.txt +0 -2
  55. fal-0.15.0/src/fal/__init__.py +0 -37
  56. fal-0.15.0/src/fal/__main__.py +0 -4
  57. fal-0.15.0/src/fal/cli.py +0 -619
  58. fal-0.15.0/src/fal/exceptions/__init__.py +0 -31
  59. fal-0.15.0/src/fal/exceptions/handlers.py +0 -58
  60. {fal-0.15.0 → fal-1.0.0}/fal.egg-info/dependency_links.txt +0 -0
  61. {fal-0.15.0 → fal-1.0.0}/fal.egg-info/top_level.txt +0 -0
  62. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/README.md +0 -0
  63. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
  64. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
  65. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
  66. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
  67. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
  68. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
  69. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
  70. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
  71. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
  72. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
  73. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_or_update_workflow_workflows_post.py +0 -0
  74. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow_workflows_user_id_workflow_name_delete.py +0 -0
  75. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/execute_workflow_workflows_user_id_workflow_name_post.py +0 -0
  76. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow_workflows_user_id_workflow_name_get.py +0 -0
  77. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflows_workflows_get.py +0 -0
  78. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
  79. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
  80. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
  81. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
  82. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
  83. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
  84. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/execute_workflow_workflows_user_id_workflow_name_post_json_body_type_0.py +0 -0
  85. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/execute_workflow_workflows_user_id_workflow_name_post_response_200_type_0.py +0 -0
  86. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
  87. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
  88. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
  89. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
  90. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
  91. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
  92. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
  93. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
  94. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
  95. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
  96. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents_type_0.py +0 -0
  97. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
  98. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
  99. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
  100. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
  101. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
  102. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
  103. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
  104. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
  105. {fal-0.15.0 → fal-1.0.0}/openapi-fal-rest/pyproject.toml +0 -0
  106. {fal-0.15.0 → fal-1.0.0}/openapi_rest.config.yaml +0 -0
  107. {fal-0.15.0 → fal-1.0.0}/setup.cfg +0 -0
  108. {fal-0.15.0 → fal-1.0.0}/src/fal/apps.py +0 -0
  109. {fal-0.15.0 → fal-1.0.0}/src/fal/console/__init__.py +0 -0
  110. {fal-0.15.0 → fal-1.0.0}/src/fal/console/icons.py +0 -0
  111. {fal-0.15.0 → fal-1.0.0}/src/fal/console/ux.py +0 -0
  112. {fal-0.15.0 → fal-1.0.0}/src/fal/exceptions/_base.py +0 -0
  113. {fal-0.15.0 → fal-1.0.0}/src/fal/exceptions/auth.py +0 -0
  114. {fal-0.15.0 → fal-1.0.0}/src/fal/logging/__init__.py +0 -0
  115. {fal-0.15.0 → fal-1.0.0}/src/fal/logging/style.py +0 -0
  116. {fal-0.15.0 → fal-1.0.0}/src/fal/logging/trace.py +0 -0
  117. {fal-0.15.0 → fal-1.0.0}/src/fal/logging/user.py +0 -0
  118. {fal-0.15.0 → fal-1.0.0}/src/fal/py.typed +0 -0
  119. {fal-0.15.0 → fal-1.0.0}/src/fal/rest_client.py +0 -0
  120. {fal-0.15.0 → fal-1.0.0}/src/fal/toolkit/__init__.py +0 -0
  121. {fal-0.15.0 → fal-1.0.0}/src/fal/toolkit/exceptions.py +0 -0
  122. {fal-0.15.0 → fal-1.0.0}/src/fal/toolkit/file/__init__.py +0 -0
  123. {fal-0.15.0 → fal-1.0.0}/src/fal/toolkit/file/providers/gcp.py +0 -0
  124. {fal-0.15.0 → fal-1.0.0}/src/fal/toolkit/file/providers/r2.py +0 -0
  125. {fal-0.15.0 → fal-1.0.0}/src/fal/toolkit/file/types.py +0 -0
  126. {fal-0.15.0 → fal-1.0.0}/src/fal/toolkit/image/__init__.py +0 -0
  127. {fal-0.15.0 → fal-1.0.0}/src/fal/toolkit/utils/__init__.py +0 -0
  128. {fal-0.15.0 → fal-1.0.0}/tests/__init__.py +0 -0
  129. {fal-0.15.0 → fal-1.0.0}/tests/mainify_package/__init__.py +0 -0
  130. {fal-0.15.0 → fal-1.0.0}/tests/mainify_package/impl.py +0 -0
  131. {fal-0.15.0 → fal-1.0.0}/tests/mainify_package/utils.py +0 -0
  132. {fal-0.15.0 → fal-1.0.0}/tests/mainify_target.py +0 -0
  133. {fal-0.15.0 → fal-1.0.0}/tools/demo_script.py +0 -0
fal-1.0.0/.gitignore ADDED
@@ -0,0 +1 @@
1
+ /src/fal/_fal_version.py
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fal
3
- Version: 0.15.0
3
+ Version: 1.0.0
4
4
  Summary: fal is an easy-to-use Serverless Python Framework
5
5
  Author: Features & Labels <hello@fal.ai>
6
6
  Requires-Python: >=3.8
7
7
  Description-Content-Type: text/markdown
8
8
  Requires-Dist: isolate[build]<1.0,>=0.12.3
9
- Requires-Dist: isolate-proto==0.3.4
9
+ Requires-Dist: isolate-proto==0.4.0
10
10
  Requires-Dist: grpcio<2,>=1.50.0
11
11
  Requires-Dist: dill==0.3.7
12
12
  Requires-Dist: cloudpickle==3.0.0
@@ -19,7 +19,7 @@ Requires-Dist: grpc-interceptor<1,>=0.15.0
19
19
  Requires-Dist: colorama<1,>=0.4.6
20
20
  Requires-Dist: portalocker<3,>=2.7.0
21
21
  Requires-Dist: rich<14,>=13.3.2
22
- Requires-Dist: rich_click
22
+ Requires-Dist: rich_argparse
23
23
  Requires-Dist: packaging<22,>=21.3
24
24
  Requires-Dist: pathspec<1,>=0.11.1
25
25
  Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3
@@ -36,9 +36,10 @@ Requires-Dist: pillow<11,>=10.2.0
36
36
  Requires-Dist: pyjwt[crypto]<3,>=2.8.0
37
37
  Requires-Dist: uvicorn<1,>=0.29.0
38
38
  Provides-Extra: test
39
- Requires-Dist: pytest; extra == "test"
40
- Requires-Dist: pytest-xdist; extra == "test"
39
+ Requires-Dist: pytest<8; extra == "test"
41
40
  Requires-Dist: pytest-asyncio; extra == "test"
41
+ Requires-Dist: pytest-xdist; extra == "test"
42
+ Requires-Dist: flaky; extra == "test"
42
43
  Provides-Extra: dev
43
44
  Requires-Dist: fal[test]; extra == "dev"
44
45
  Requires-Dist: openapi-python-client<1,>=0.14.1; extra == "dev"
@@ -89,3 +90,30 @@ A new virtual environment will be created by fal in the cloud and the set of req
89
90
  ## Next steps
90
91
 
91
92
  If you would like to find out more about the capabilities of fal, check out to the [docs](https://fal.ai/docs). You can learn more about persistent storage, function caches and deploying your functions as API endpoints.
93
+
94
+ ## Contributing
95
+
96
+ ### Installing in editable mode with dev dependencies
97
+
98
+ ```py
99
+ pip install -e 'projects/fal[dev]'
100
+ pip install -e 'projects/fal_client[dev]'
101
+ pip install -e 'projects/isolate_proto[dev]'
102
+ ```
103
+
104
+ ### Running tests
105
+
106
+ ```py
107
+ pytest
108
+ ```
109
+
110
+ ### Pre-commit
111
+
112
+ ```
113
+ cd projects/fal
114
+ pre-commit install
115
+ ```
116
+
117
+ ### Commit format
118
+
119
+ Please follow [conventional commits specification](https://www.conventionalcommits.org/) for descriptions/messages.
@@ -44,3 +44,30 @@ A new virtual environment will be created by fal in the cloud and the set of req
44
44
  ## Next steps
45
45
 
46
46
  If you would like to find out more about the capabilities of fal, check out to the [docs](https://fal.ai/docs). You can learn more about persistent storage, function caches and deploying your functions as API endpoints.
47
+
48
+ ## Contributing
49
+
50
+ ### Installing in editable mode with dev dependencies
51
+
52
+ ```py
53
+ pip install -e 'projects/fal[dev]'
54
+ pip install -e 'projects/fal_client[dev]'
55
+ pip install -e 'projects/isolate_proto[dev]'
56
+ ```
57
+
58
+ ### Running tests
59
+
60
+ ```py
61
+ pytest
62
+ ```
63
+
64
+ ### Pre-commit
65
+
66
+ ```
67
+ cd projects/fal
68
+ pre-commit install
69
+ ```
70
+
71
+ ### Commit format
72
+
73
+ Please follow [conventional commits specification](https://www.conventionalcommits.org/) for descriptions/messages.
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fal
3
- Version: 0.15.0
3
+ Version: 1.0.0
4
4
  Summary: fal is an easy-to-use Serverless Python Framework
5
5
  Author: Features & Labels <hello@fal.ai>
6
6
  Requires-Python: >=3.8
7
7
  Description-Content-Type: text/markdown
8
8
  Requires-Dist: isolate[build]<1.0,>=0.12.3
9
- Requires-Dist: isolate-proto==0.3.4
9
+ Requires-Dist: isolate-proto==0.4.0
10
10
  Requires-Dist: grpcio<2,>=1.50.0
11
11
  Requires-Dist: dill==0.3.7
12
12
  Requires-Dist: cloudpickle==3.0.0
@@ -19,7 +19,7 @@ Requires-Dist: grpc-interceptor<1,>=0.15.0
19
19
  Requires-Dist: colorama<1,>=0.4.6
20
20
  Requires-Dist: portalocker<3,>=2.7.0
21
21
  Requires-Dist: rich<14,>=13.3.2
22
- Requires-Dist: rich_click
22
+ Requires-Dist: rich_argparse
23
23
  Requires-Dist: packaging<22,>=21.3
24
24
  Requires-Dist: pathspec<1,>=0.11.1
25
25
  Requires-Dist: pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3
@@ -36,9 +36,10 @@ Requires-Dist: pillow<11,>=10.2.0
36
36
  Requires-Dist: pyjwt[crypto]<3,>=2.8.0
37
37
  Requires-Dist: uvicorn<1,>=0.29.0
38
38
  Provides-Extra: test
39
- Requires-Dist: pytest; extra == "test"
40
- Requires-Dist: pytest-xdist; extra == "test"
39
+ Requires-Dist: pytest<8; extra == "test"
41
40
  Requires-Dist: pytest-asyncio; extra == "test"
41
+ Requires-Dist: pytest-xdist; extra == "test"
42
+ Requires-Dist: flaky; extra == "test"
42
43
  Provides-Extra: dev
43
44
  Requires-Dist: fal[test]; extra == "dev"
44
45
  Requires-Dist: openapi-python-client<1,>=0.14.1; extra == "dev"
@@ -89,3 +90,30 @@ A new virtual environment will be created by fal in the cloud and the set of req
89
90
  ## Next steps
90
91
 
91
92
  If you would like to find out more about the capabilities of fal, check out to the [docs](https://fal.ai/docs). You can learn more about persistent storage, function caches and deploying your functions as API endpoints.
93
+
94
+ ## Contributing
95
+
96
+ ### Installing in editable mode with dev dependencies
97
+
98
+ ```py
99
+ pip install -e 'projects/fal[dev]'
100
+ pip install -e 'projects/fal_client[dev]'
101
+ pip install -e 'projects/isolate_proto[dev]'
102
+ ```
103
+
104
+ ### Running tests
105
+
106
+ ```py
107
+ pytest
108
+ ```
109
+
110
+ ### Pre-commit
111
+
112
+ ```
113
+ cd projects/fal
114
+ pre-commit install
115
+ ```
116
+
117
+ ### Commit format
118
+
119
+ Please follow [conventional commits specification](https://www.conventionalcommits.org/) for descriptions/messages.
@@ -1,3 +1,4 @@
1
+ .gitignore
1
2
  README.md
2
3
  openapi_rest.config.yaml
3
4
  pyproject.toml
@@ -53,27 +54,38 @@ openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py
53
54
  openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py
54
55
  src/fal/__init__.py
55
56
  src/fal/__main__.py
57
+ src/fal/_fal_version.py
56
58
  src/fal/_serialization.py
59
+ src/fal/_version.py
57
60
  src/fal/api.py
58
61
  src/fal/app.py
59
62
  src/fal/apps.py
60
- src/fal/cli.py
61
63
  src/fal/flags.py
62
64
  src/fal/py.typed
63
65
  src/fal/rest_client.py
64
66
  src/fal/sdk.py
65
67
  src/fal/sync.py
68
+ src/fal/utils.py
66
69
  src/fal/workflows.py
67
70
  src/fal/auth/__init__.py
68
71
  src/fal/auth/auth0.py
69
72
  src/fal/auth/local.py
73
+ src/fal/cli/__init__.py
74
+ src/fal/cli/apps.py
75
+ src/fal/cli/auth.py
76
+ src/fal/cli/debug.py
77
+ src/fal/cli/deploy.py
78
+ src/fal/cli/keys.py
79
+ src/fal/cli/main.py
80
+ src/fal/cli/parser.py
81
+ src/fal/cli/run.py
82
+ src/fal/cli/secrets.py
70
83
  src/fal/console/__init__.py
71
84
  src/fal/console/icons.py
72
85
  src/fal/console/ux.py
73
86
  src/fal/exceptions/__init__.py
74
87
  src/fal/exceptions/_base.py
75
88
  src/fal/exceptions/auth.py
76
- src/fal/exceptions/handlers.py
77
89
  src/fal/logging/__init__.py
78
90
  src/fal/logging/isolate.py
79
91
  src/fal/logging/style.py
@@ -98,6 +110,13 @@ tests/integration_test.py
98
110
  tests/mainify_target.py
99
111
  tests/test_apps.py
100
112
  tests/test_stability.py
113
+ tests/cli/__init__.py
114
+ tests/cli/test_apps.py
115
+ tests/cli/test_auth.py
116
+ tests/cli/test_deploy.py
117
+ tests/cli/test_keys.py
118
+ tests/cli/test_run.py
119
+ tests/cli/test_secrets.py
101
120
  tests/mainify_package/__init__.py
102
121
  tests/mainify_package/impl.py
103
122
  tests/mainify_package/utils.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ fal = fal.cli:main
@@ -1,5 +1,5 @@
1
1
  isolate[build]<1.0,>=0.12.3
2
- isolate-proto==0.3.4
2
+ isolate-proto==0.4.0
3
3
  grpcio<2,>=1.50.0
4
4
  dill==0.3.7
5
5
  cloudpickle==3.0.0
@@ -12,7 +12,7 @@ grpc-interceptor<1,>=0.15.0
12
12
  colorama<1,>=0.4.6
13
13
  portalocker<3,>=2.7.0
14
14
  rich<14,>=13.3.2
15
- rich_click
15
+ rich_argparse
16
16
  packaging<22,>=21.3
17
17
  pathspec<1,>=0.11.1
18
18
  pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3
@@ -36,6 +36,7 @@ fal[test]
36
36
  openapi-python-client<1,>=0.14.1
37
37
 
38
38
  [test]
39
- pytest
40
- pytest-xdist
39
+ pytest<8
41
40
  pytest-asyncio
41
+ pytest-xdist
42
+ flaky
@@ -7,6 +7,7 @@ root = "../../"
7
7
  git_describe_command= 'git describe --tags --abbrev=0 --dirty --match "fal_v*"'
8
8
  tag_regex = "^fal_v(?P<version>.*)$"
9
9
  fallback_version = "0.0.0"
10
+ version_file = "src/fal/_fal_version.py"
10
11
 
11
12
  [tool.setuptools.packages.find]
12
13
  where = ["src", "openapi-fal-rest"]
@@ -22,7 +23,7 @@ readme = "README.md"
22
23
  requires-python = ">=3.8"
23
24
  dependencies = [
24
25
  "isolate[build]>=0.12.3,<1.0",
25
- "isolate-proto==0.3.4",
26
+ "isolate-proto==0.4.0",
26
27
  "grpcio>=1.50.0,<2",
27
28
  "dill==0.3.7",
28
29
  "cloudpickle==3.0.0",
@@ -35,7 +36,7 @@ dependencies = [
35
36
  "colorama>=0.4.6,<1",
36
37
  "portalocker>=2.7.0,<3",
37
38
  "rich>=13.3.2,<14",
38
- "rich_click",
39
+ "rich_argparse",
39
40
  "packaging>=21.3,<22",
40
41
  "pathspec>=0.11.1,<1",
41
42
  "pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,<3",
@@ -58,9 +59,10 @@ dependencies = [
58
59
 
59
60
  [project.optional-dependencies]
60
61
  test = [
61
- "pytest",
62
- "pytest-xdist",
62
+ "pytest<8",
63
63
  "pytest-asyncio",
64
+ "pytest-xdist",
65
+ "flaky",
64
66
  ]
65
67
  dev = [
66
68
  "fal[test]",
@@ -68,4 +70,13 @@ dev = [
68
70
  ]
69
71
 
70
72
  [project.scripts]
71
- fal = "fal.cli:cli"
73
+ fal = "fal.cli:main"
74
+
75
+ [tool.ruff]
76
+ target-version = "py38"
77
+
78
+ [tool.ruff.lint]
79
+ select = ["E", "F", "W", "PLC", "PLE", "PLW", "I", "UP"]
80
+
81
+ [tool.ruff.lint.pyupgrade]
82
+ keep-runtime-typing = true
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from fal import apps # noqa: F401
4
+ from fal.api import FalServerlessHost, LocalHost, cached, function
5
+ from fal.api import function as isolated # noqa: F401
6
+ from fal.app import App, endpoint, realtime, wrap_app # noqa: F401
7
+ from fal.sdk import FalServerlessKeyCredentials
8
+ from fal.sync import sync_dir
9
+
10
+ from ._version import __version__, version_tuple # noqa: F401
11
+
12
+ local = LocalHost()
13
+ serverless = FalServerlessHost()
14
+
15
+ # DEPRECATED - use serverless instead
16
+ cloud = FalServerlessHost()
17
+
18
+ __all__ = [
19
+ "function",
20
+ "cached",
21
+ "App",
22
+ "endpoint",
23
+ "realtime",
24
+ # "wrap_app",
25
+ "FalServerlessKeyCredentials",
26
+ "sync_dir",
27
+ "__version__",
28
+ "version_tuple",
29
+ ]
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,16 @@
1
+ # file generated by setuptools_scm
2
+ # don't change, don't track in version control
3
+ TYPE_CHECKING = False
4
+ if TYPE_CHECKING:
5
+ from typing import Tuple, Union
6
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
7
+ else:
8
+ VERSION_TUPLE = object
9
+
10
+ version: str
11
+ __version__: str
12
+ __version_tuple__: VERSION_TUPLE
13
+ version_tuple: VERSION_TUPLE
14
+
15
+ __version__ = version = '1.0.0'
16
+ __version_tuple__ = version_tuple = (1, 0, 0)
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import pickle
3
4
  from typing import Any, Callable
4
5
 
5
- import pickle
6
6
  import cloudpickle
7
7
 
8
8
 
@@ -98,11 +98,12 @@ def _patch_pydantic_field_serialization() -> None:
98
98
 
99
99
 
100
100
  def _patch_pydantic_model_serialization() -> None:
101
- # If user has created new pydantic models in his namespace, we will try to pickle those
102
- # by value, which means recreating class skeleton, which will stumble upon
103
- # __pydantic_parent_namespace__ in its __dict__ and it may contain modules that happened
104
- # to be imported in the namespace but are not actually used, resulting in pickling errors.
105
- # Unfortunately this also means that `model_rebuid()` might not work.
101
+ # If user has created new pydantic models in his namespace, we will try to pickle
102
+ # those by value, which means recreating class skeleton, which will stumble upon
103
+ # __pydantic_parent_namespace__ in its __dict__ and it may contain modules that
104
+ # happened to be imported in the namespace but are not actually used, resulting
105
+ # in pickling errors. Unfortunately this also means that `model_rebuid()` might
106
+ # not work.
106
107
  try:
107
108
  import pydantic
108
109
  except ImportError:
@@ -133,7 +134,8 @@ def _patch_lru_cache() -> None:
133
134
  # https://github.com/cloudpipe/cloudpickle/issues/178
134
135
  # https://github.com/uqfoundation/dill/blob/70f569b0dd268d2b1e85c0f300951b11f53c5d53/dill/_dill.py#L1429
135
136
 
136
- from functools import lru_cache, _lru_cache_wrapper as LRUCacheType
137
+ from functools import _lru_cache_wrapper as LRUCacheType
138
+ from functools import lru_cache
137
139
 
138
140
  def create_lru_cache(func: Callable, kwargs: dict) -> LRUCacheType:
139
141
  return lru_cache(**kwargs)(func)
@@ -155,8 +157,8 @@ def _patch_lru_cache() -> None:
155
157
 
156
158
  def _patch_lock() -> None:
157
159
  # https://github.com/uqfoundation/dill/blob/70f569b0dd268d2b1e85c0f300951b11f53c5d53/dill/_dill.py#L1310
158
- from threading import Lock
159
160
  from _thread import LockType
161
+ from threading import Lock
160
162
 
161
163
  def create_lock(locked: bool) -> Lock:
162
164
  lock = Lock()
@@ -199,7 +201,11 @@ def _patch_console_thread_locals() -> None:
199
201
  return ConsoleThreadLocals(**kwargs)
200
202
 
201
203
  def pickle_locals(obj: ConsoleThreadLocals) -> tuple[Callable, tuple]:
202
- kwargs = {"theme_stack": obj.theme_stack, "buffer": obj.buffer, "buffer_index": obj.buffer_index}
204
+ kwargs = {
205
+ "theme_stack": obj.theme_stack,
206
+ "buffer": obj.buffer,
207
+ "buffer_index": obj.buffer_index,
208
+ }
203
209
  return create_locals, (kwargs, )
204
210
 
205
211
  _register(ConsoleThreadLocals, pickle_locals)
@@ -0,0 +1,6 @@
1
+ try:
2
+ from ._fal_version import version as __version__ # type: ignore[import]
3
+ from ._fal_version import version_tuple # type: ignore[import]
4
+ except ImportError:
5
+ __version__ = "UNKNOWN"
6
+ version_tuple = (0, 0, __version__) # type: ignore[assignment]
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import inspect
4
+ import os
4
5
  import sys
5
6
  import threading
6
7
  from collections import defaultdict
@@ -40,8 +41,8 @@ from pydantic import __version__ as pydantic_version
40
41
  from typing_extensions import Concatenate, ParamSpec
41
42
 
42
43
  import fal.flags as flags
43
- from fal.exceptions import FalServerlessException
44
44
  from fal._serialization import include_modules_from, patch_pickle
45
+ from fal.exceptions import FalServerlessException
45
46
  from fal.logging.isolate import IsolateLogPrinter
46
47
  from fal.sdk import (
47
48
  FAL_SERVERLESS_DEFAULT_KEEP_ALIVE,
@@ -57,7 +58,7 @@ from fal.sdk import (
57
58
  )
58
59
 
59
60
  ArgsT = ParamSpec("ArgsT")
60
- ReturnT = TypeVar("ReturnT", covariant=True)
61
+ ReturnT = TypeVar("ReturnT", covariant=True) # noqa: PLC0105
61
62
 
62
63
  BasicConfig = Dict[str, Any]
63
64
  _UNSET = object()
@@ -113,8 +114,8 @@ class Host(Generic[ArgsT, ReturnT]):
113
114
  environment options."""
114
115
 
115
116
  options = Options()
116
- for key, value in config.items():
117
- key, value = cls.parse_key(key, value)
117
+ for item in config.items():
118
+ key, value = cls.parse_key(*item)
118
119
  if key in cls._SUPPORTED_KEYS:
119
120
  options.host[key] = value
120
121
  elif key in cls._GATEWAY_KEYS:
@@ -545,7 +546,8 @@ _DEFAULT_HOST = FalServerlessHost()
545
546
  _SERVE_PORT = 8080
546
547
 
547
548
  # Overload @function to help users identify the correct signature.
548
- # NOTE: This is both in sync with host options and with environment configs from `isolate` package.
549
+ # NOTE: This is both in sync with host options and with environment configs from
550
+ # `isolate` package.
549
551
 
550
552
 
551
553
  ## virtualenv
@@ -785,7 +787,8 @@ class FalFastAPI(FastAPI):
785
787
  """
786
788
  Add x-fal-order-* keys to the OpenAPI specification to help the rendering of UI.
787
789
 
788
- NOTE: We rely on the fact that fastapi and Python dicts keep the order of properties.
790
+ NOTE: We rely on the fact that fastapi and Python dicts keep the order of
791
+ properties.
789
792
  """
790
793
 
791
794
  def mark_order(obj: dict[str, Any], key: str):
@@ -830,6 +833,9 @@ class BaseServable:
830
833
  """
831
834
  pass
832
835
 
836
+ def _add_extra_routes(self, app: FastAPI):
837
+ pass
838
+
833
839
  @asynccontextmanager
834
840
  async def lifespan(self, app: FastAPI):
835
841
  yield
@@ -840,7 +846,10 @@ class BaseServable:
840
846
  from fastapi.responses import JSONResponse
841
847
  from starlette_exporter import PrometheusMiddleware
842
848
 
843
- _app = FalFastAPI(lifespan=self.lifespan)
849
+ _app = FalFastAPI(
850
+ lifespan=self.lifespan,
851
+ root_path=os.getenv("FAL_APP_ROOT_PATH") or "",
852
+ )
844
853
 
845
854
  _app.add_middleware(
846
855
  CORSMiddleware,
@@ -891,6 +900,8 @@ class BaseServable:
891
900
  methods=["POST"],
892
901
  )
893
902
 
903
+ self._add_extra_routes(_app)
904
+
894
905
  return _app
895
906
 
896
907
  def openapi(self) -> dict[str, Any]:
@@ -918,7 +929,9 @@ class BaseServable:
918
929
  asyncio.create_task(metrics_server.serve()): metrics_server,
919
930
  }
920
931
 
921
- _, pending = await asyncio.wait(tasks.keys(), return_when=asyncio.FIRST_COMPLETED)
932
+ _, pending = await asyncio.wait(
933
+ tasks.keys(), return_when=asyncio.FIRST_COMPLETED,
934
+ )
922
935
  if not pending:
923
936
  return
924
937
 
@@ -1007,13 +1020,15 @@ class IsolatedFunction(Generic[ArgsT, ReturnT]):
1007
1020
  lines = []
1008
1021
  for used_modules, references in pairs:
1009
1022
  lines.append(
1010
- f"\t- {used_modules!r} (accessed through {', '.join(map(repr, references))})"
1023
+ f"\t- {used_modules!r} "
1024
+ f"(accessed through {', '.join(map(repr, references))})"
1011
1025
  )
1012
1026
 
1013
1027
  function_name = self.func.__name__
1014
1028
  raise FalServerlessError(
1015
- f"Couldn't deserialize your function on the remote server. \n\n[Hint] {function_name!r} "
1016
- f"function uses the following modules which weren't present in the environment definition:\n"
1029
+ f"Couldn't deserialize your function on the remote server. \n\n"
1030
+ f"[Hint] {function_name!r} function uses the following modules "
1031
+ "which weren't present in the environment definition:\n"
1017
1032
  + "\n".join(lines)
1018
1033
  ) from None
1019
1034
  except Exception as exc:
@@ -1065,7 +1080,8 @@ class IsolatedFunction(Generic[ArgsT, ReturnT]):
1065
1080
  def func(self) -> Callable[ArgsT, ReturnT]:
1066
1081
  serve_mode = self.options.gateway.get("serve")
1067
1082
  if serve_mode:
1068
- # This type can be safely ignored because this case only happens when it is a ServedIsolatedFunction
1083
+ # This type can be safely ignored because this case only happens when it
1084
+ # is a ServedIsolatedFunction
1069
1085
  serve_func: Callable[[], None] = ServeWrapper(self.raw_func)
1070
1086
  return serve_func # type: ignore
1071
1087
  else:
@@ -1098,8 +1114,10 @@ class ServedIsolatedFunction(
1098
1114
 
1099
1115
  class Server(uvicorn.Server):
1100
1116
  """Server is a uvicorn.Server that actually plays nicely with signals.
1101
- By default, uvicorn's Server class overwrites the signal handler for SIGINT, swallowing the signal and preventing other tasks from cancelling.
1102
- This class allows the task to be gracefully cancelled using asyncio's built-in task cancellation or with an event, like aiohttp.
1117
+ By default, uvicorn's Server class overwrites the signal handler for SIGINT,
1118
+ swallowing the signal and preventing other tasks from cancelling.
1119
+ This class allows the task to be gracefully cancelled using asyncio's built-in task
1120
+ cancellation or with an event, like aiohttp.
1103
1121
  """
1104
1122
 
1105
1123
  def install_signal_handlers(self) -> None: