fal 1.6.0__tar.gz → 1.6.2__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 (168) hide show
  1. {fal-1.6.0/fal.egg-info → fal-1.6.2}/PKG-INFO +2 -2
  2. {fal-1.6.0 → fal-1.6.2/fal.egg-info}/PKG-INFO +2 -2
  3. {fal-1.6.0 → fal-1.6.2}/pyproject.toml +3 -0
  4. {fal-1.6.0 → fal-1.6.2}/src/fal/_fal_version.py +2 -2
  5. {fal-1.6.0 → fal-1.6.2}/src/fal/app.py +9 -0
  6. {fal-1.6.0 → fal-1.6.2}/src/fal/cli/_utils.py +2 -1
  7. {fal-1.6.0 → fal-1.6.2}/src/fal/cli/deploy.py +7 -2
  8. {fal-1.6.0 → fal-1.6.2}/src/fal/cli/run.py +1 -1
  9. {fal-1.6.0 → fal-1.6.2}/src/fal/container.py +1 -1
  10. {fal-1.6.0 → fal-1.6.2}/tests/cli/test_deploy.py +54 -0
  11. {fal-1.6.0 → fal-1.6.2}/tests/cli/test_run.py +1 -0
  12. {fal-1.6.0 → fal-1.6.2}/tests/conftest.py +7 -0
  13. {fal-1.6.0 → fal-1.6.2}/tests/integration_test.py +4 -3
  14. {fal-1.6.0 → fal-1.6.2}/tests/test_apps.py +51 -8
  15. {fal-1.6.0 → fal-1.6.2}/tests/test_stability.py +9 -4
  16. {fal-1.6.0 → fal-1.6.2}/tests/toolkit/file_test.py +1 -0
  17. {fal-1.6.0 → fal-1.6.2}/tests/toolkit/image_test.py +2 -1
  18. {fal-1.6.0 → fal-1.6.2}/tests/toolkit/utils/retry.py +1 -0
  19. {fal-1.6.0 → fal-1.6.2}/.gitignore +0 -0
  20. {fal-1.6.0 → fal-1.6.2}/Makefile +0 -0
  21. {fal-1.6.0 → fal-1.6.2}/README.md +0 -0
  22. {fal-1.6.0 → fal-1.6.2}/docs/conf.py +0 -0
  23. {fal-1.6.0 → fal-1.6.2}/docs/index.rst +0 -0
  24. {fal-1.6.0 → fal-1.6.2}/fal.egg-info/SOURCES.txt +0 -0
  25. {fal-1.6.0 → fal-1.6.2}/fal.egg-info/dependency_links.txt +0 -0
  26. {fal-1.6.0 → fal-1.6.2}/fal.egg-info/entry_points.txt +0 -0
  27. {fal-1.6.0 → fal-1.6.2}/fal.egg-info/requires.txt +0 -0
  28. {fal-1.6.0 → fal-1.6.2}/fal.egg-info/top_level.txt +0 -0
  29. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/README.md +0 -0
  30. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
  31. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
  32. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
  33. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
  34. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
  35. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
  36. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
  37. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
  38. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
  39. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
  40. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
  41. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
  42. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
  43. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
  44. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
  45. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
  46. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
  47. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
  48. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
  49. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
  50. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
  51. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
  52. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
  53. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
  54. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
  55. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
  56. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
  57. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
  58. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
  59. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
  60. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
  61. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
  62. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
  63. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
  64. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
  65. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
  66. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
  67. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
  68. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
  69. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
  70. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
  71. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
  72. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
  73. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
  74. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
  75. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
  76. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
  77. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
  78. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
  79. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
  80. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
  81. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
  82. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
  83. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
  84. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
  85. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
  86. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
  87. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
  88. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
  89. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
  90. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
  91. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
  92. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
  93. {fal-1.6.0 → fal-1.6.2}/openapi-fal-rest/pyproject.toml +0 -0
  94. {fal-1.6.0 → fal-1.6.2}/openapi_rest.config.yaml +0 -0
  95. {fal-1.6.0 → fal-1.6.2}/setup.cfg +0 -0
  96. {fal-1.6.0 → fal-1.6.2}/src/fal/__init__.py +0 -0
  97. {fal-1.6.0 → fal-1.6.2}/src/fal/__main__.py +0 -0
  98. {fal-1.6.0 → fal-1.6.2}/src/fal/_serialization.py +0 -0
  99. {fal-1.6.0 → fal-1.6.2}/src/fal/_version.py +0 -0
  100. {fal-1.6.0 → fal-1.6.2}/src/fal/api.py +0 -0
  101. {fal-1.6.0 → fal-1.6.2}/src/fal/apps.py +0 -0
  102. {fal-1.6.0 → fal-1.6.2}/src/fal/auth/__init__.py +0 -0
  103. {fal-1.6.0 → fal-1.6.2}/src/fal/auth/auth0.py +0 -0
  104. {fal-1.6.0 → fal-1.6.2}/src/fal/auth/local.py +0 -0
  105. {fal-1.6.0 → fal-1.6.2}/src/fal/cli/__init__.py +0 -0
  106. {fal-1.6.0 → fal-1.6.2}/src/fal/cli/apps.py +0 -0
  107. {fal-1.6.0 → fal-1.6.2}/src/fal/cli/auth.py +0 -0
  108. {fal-1.6.0 → fal-1.6.2}/src/fal/cli/create.py +0 -0
  109. {fal-1.6.0 → fal-1.6.2}/src/fal/cli/debug.py +0 -0
  110. {fal-1.6.0 → fal-1.6.2}/src/fal/cli/doctor.py +0 -0
  111. {fal-1.6.0 → fal-1.6.2}/src/fal/cli/keys.py +0 -0
  112. {fal-1.6.0 → fal-1.6.2}/src/fal/cli/main.py +0 -0
  113. {fal-1.6.0 → fal-1.6.2}/src/fal/cli/parser.py +0 -0
  114. {fal-1.6.0 → fal-1.6.2}/src/fal/cli/runners.py +0 -0
  115. {fal-1.6.0 → fal-1.6.2}/src/fal/cli/secrets.py +0 -0
  116. {fal-1.6.0 → fal-1.6.2}/src/fal/console/__init__.py +0 -0
  117. {fal-1.6.0 → fal-1.6.2}/src/fal/console/icons.py +0 -0
  118. {fal-1.6.0 → fal-1.6.2}/src/fal/console/ux.py +0 -0
  119. {fal-1.6.0 → fal-1.6.2}/src/fal/exceptions/__init__.py +0 -0
  120. {fal-1.6.0 → fal-1.6.2}/src/fal/exceptions/_base.py +0 -0
  121. {fal-1.6.0 → fal-1.6.2}/src/fal/exceptions/_cuda.py +0 -0
  122. {fal-1.6.0 → fal-1.6.2}/src/fal/exceptions/auth.py +0 -0
  123. {fal-1.6.0 → fal-1.6.2}/src/fal/files.py +0 -0
  124. {fal-1.6.0 → fal-1.6.2}/src/fal/flags.py +0 -0
  125. {fal-1.6.0 → fal-1.6.2}/src/fal/logging/__init__.py +0 -0
  126. {fal-1.6.0 → fal-1.6.2}/src/fal/logging/isolate.py +0 -0
  127. {fal-1.6.0 → fal-1.6.2}/src/fal/logging/style.py +0 -0
  128. {fal-1.6.0 → fal-1.6.2}/src/fal/logging/trace.py +0 -0
  129. {fal-1.6.0 → fal-1.6.2}/src/fal/logging/user.py +0 -0
  130. {fal-1.6.0 → fal-1.6.2}/src/fal/py.typed +0 -0
  131. {fal-1.6.0 → fal-1.6.2}/src/fal/rest_client.py +0 -0
  132. {fal-1.6.0 → fal-1.6.2}/src/fal/sdk.py +0 -0
  133. {fal-1.6.0 → fal-1.6.2}/src/fal/sync.py +0 -0
  134. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/__init__.py +0 -0
  135. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/exceptions.py +0 -0
  136. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/file/__init__.py +0 -0
  137. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/file/file.py +0 -0
  138. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/file/providers/fal.py +0 -0
  139. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/file/providers/gcp.py +0 -0
  140. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/file/providers/r2.py +0 -0
  141. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/file/providers/s3.py +0 -0
  142. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/file/types.py +0 -0
  143. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/image/__init__.py +0 -0
  144. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/image/image.py +0 -0
  145. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
  146. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
  147. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
  148. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
  149. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
  150. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/image/safety_checker.py +0 -0
  151. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/optimize.py +0 -0
  152. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/utils/__init__.py +0 -0
  153. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/utils/download_utils.py +0 -0
  154. {fal-1.6.0 → fal-1.6.2}/src/fal/toolkit/utils/retry.py +0 -0
  155. {fal-1.6.0 → fal-1.6.2}/src/fal/utils.py +0 -0
  156. {fal-1.6.0 → fal-1.6.2}/src/fal/workflows.py +0 -0
  157. {fal-1.6.0 → fal-1.6.2}/tests/__init__.py +0 -0
  158. {fal-1.6.0 → fal-1.6.2}/tests/assets/cat.png +0 -0
  159. {fal-1.6.0 → fal-1.6.2}/tests/cli/__init__.py +0 -0
  160. {fal-1.6.0 → fal-1.6.2}/tests/cli/test_apps.py +0 -0
  161. {fal-1.6.0 → fal-1.6.2}/tests/cli/test_auth.py +0 -0
  162. {fal-1.6.0 → fal-1.6.2}/tests/cli/test_keys.py +0 -0
  163. {fal-1.6.0 → fal-1.6.2}/tests/cli/test_secrets.py +0 -0
  164. {fal-1.6.0 → fal-1.6.2}/tests/mainify_package/__init__.py +0 -0
  165. {fal-1.6.0 → fal-1.6.2}/tests/mainify_package/impl.py +0 -0
  166. {fal-1.6.0 → fal-1.6.2}/tests/mainify_package/utils.py +0 -0
  167. {fal-1.6.0 → fal-1.6.2}/tests/mainify_target.py +0 -0
  168. {fal-1.6.0 → fal-1.6.2}/tools/demo_script.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: fal
3
- Version: 1.6.0
3
+ Version: 1.6.2
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
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: fal
3
- Version: 1.6.0
3
+ Version: 1.6.2
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
@@ -87,5 +87,8 @@ target-version = "py38"
87
87
  [tool.ruff.lint]
88
88
  select = ["E", "F", "W", "PLC", "PLE", "PLW", "I", "UP"]
89
89
 
90
+ [tool.ruff.lint.isort]
91
+ known-first-party = ["fal"]
92
+
90
93
  [tool.ruff.lint.pyupgrade]
91
94
  keep-runtime-typing = true
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.6.0'
16
- __version_tuple__ = version_tuple = (1, 6, 0)
15
+ __version__ = version = '1.6.2'
16
+ __version_tuple__ = version_tuple = (1, 6, 2)
@@ -248,6 +248,14 @@ def _to_fal_app_name(name: str) -> str:
248
248
  return "-".join(part.lower() for part in PART_FINDER_RE.findall(name))
249
249
 
250
250
 
251
+ def _print_python_packages() -> None:
252
+ from pkg_resources import working_set
253
+
254
+ packages = [f"{package.key}=={package.version}" for package in working_set]
255
+
256
+ print("[debug] Python packages installed:", ", ".join(packages))
257
+
258
+
251
259
  class App(fal.api.BaseServable):
252
260
  requirements: ClassVar[list[str]] = []
253
261
  machine_type: ClassVar[str] = "S"
@@ -305,6 +313,7 @@ class App(fal.api.BaseServable):
305
313
 
306
314
  @asynccontextmanager
307
315
  async def lifespan(self, app: fastapi.FastAPI):
316
+ _print_python_packages()
308
317
  await _call_any_fn(self.setup)
309
318
  try:
310
319
  yield
@@ -35,5 +35,6 @@ def get_app_data_from_toml(app_name):
35
35
 
36
36
  app_auth = app_data.get("auth", "private")
37
37
  app_deployment_strategy = app_data.get("deployment_strategy", "recreate")
38
+ app_no_scale = app_data.get("no_scale", False)
38
39
 
39
- return app_ref, app_auth, app_deployment_strategy
40
+ return app_ref, app_auth, app_deployment_strategy, app_no_scale
@@ -68,6 +68,7 @@ def _deploy_from_reference(
68
68
  args,
69
69
  auth: Optional[Literal["public", "shared", "private"]] = None,
70
70
  deployment_strategy: Optional[Literal["recreate", "rolling"]] = None,
71
+ no_scale: bool = False,
71
72
  ):
72
73
  from fal.api import FalServerlessError, FalServerlessHost
73
74
  from fal.utils import load_function_from
@@ -106,7 +107,7 @@ def _deploy_from_reference(
106
107
  application_auth_mode=app_auth,
107
108
  metadata=isolated_function.options.host.get("metadata", {}),
108
109
  deployment_strategy=deployment_strategy,
109
- scale=not args.no_scale,
110
+ scale=not no_scale,
110
111
  )
111
112
 
112
113
  if app_id:
@@ -139,7 +140,9 @@ def _deploy(args):
139
140
  raise ValueError("Cannot use --app-name or --auth with app name reference.")
140
141
 
141
142
  app_name = args.app_ref[0]
142
- app_ref, app_auth, app_deployment_strategy = get_app_data_from_toml(app_name)
143
+ app_ref, app_auth, app_deployment_strategy, app_no_scale = (
144
+ get_app_data_from_toml(app_name)
145
+ )
143
146
  file_path, func_name = RefAction.split_ref(app_ref)
144
147
 
145
148
  # path/to/myfile.py::MyApp
@@ -148,6 +151,7 @@ def _deploy(args):
148
151
  app_name = args.app_name
149
152
  app_auth = args.auth
150
153
  app_deployment_strategy = args.strategy
154
+ app_no_scale = args.no_scale
151
155
 
152
156
  _deploy_from_reference(
153
157
  (file_path, func_name),
@@ -155,6 +159,7 @@ def _deploy(args):
155
159
  args,
156
160
  app_auth,
157
161
  app_deployment_strategy,
162
+ app_no_scale,
158
163
  )
159
164
 
160
165
 
@@ -10,7 +10,7 @@ def _run(args):
10
10
 
11
11
  if is_app_name(args.func_ref):
12
12
  app_name = args.func_ref[0]
13
- app_ref, _, _ = get_app_data_from_toml(app_name)
13
+ app_ref, *_ = get_app_data_from_toml(app_name)
14
14
  file_path, func_name = RefAction.split_ref(app_ref)
15
15
  else:
16
16
  file_path, func_name = args.func_ref
@@ -3,7 +3,7 @@ class ContainerImage:
3
3
  from a Dockerfile.
4
4
  """
5
5
 
6
- _known_keys = {"dockerfile_str", "build_env", "build_args"}
6
+ _known_keys = {"dockerfile_str", "build_args", "registries"}
7
7
 
8
8
  @classmethod
9
9
  def from_dockerfile_str(cls, text: str, **kwargs):
@@ -2,6 +2,7 @@ from typing import Optional, Tuple
2
2
  from unittest.mock import MagicMock, patch
3
3
 
4
4
  import pytest
5
+
5
6
  from fal.cli.deploy import _deploy
6
7
  from fal.cli.main import parse_args
7
8
  from fal.files import find_project_root
@@ -34,6 +35,7 @@ def mock_args(
34
35
  app_name: Optional[str] = None,
35
36
  auth: Optional[str] = None,
36
37
  strategy: Optional[str] = None,
38
+ no_scale: bool = False,
37
39
  ):
38
40
  args = MagicMock()
39
41
 
@@ -42,6 +44,8 @@ def mock_args(
42
44
  args.auth = auth
43
45
  args.strategy = strategy
44
46
 
47
+ args.no_scale = no_scale
48
+
45
49
  return args
46
50
 
47
51
 
@@ -67,6 +71,7 @@ def test_deploy_with_toml_success(
67
71
  args,
68
72
  "shared",
69
73
  "rolling",
74
+ False,
70
75
  )
71
76
 
72
77
 
@@ -92,6 +97,7 @@ def test_deploy_with_toml_no_auth(
92
97
  args,
93
98
  "private",
94
99
  "recreate",
100
+ False,
95
101
  )
96
102
 
97
103
 
@@ -185,6 +191,7 @@ def test_deploy_with_toml_deployment_strategy(
185
191
  args,
186
192
  "shared",
187
193
  "rolling",
194
+ False,
188
195
  )
189
196
 
190
197
 
@@ -208,6 +215,7 @@ def test_deploy_with_toml_default_deployment_strategy(
208
215
  args,
209
216
  "private",
210
217
  "recreate",
218
+ False,
211
219
  )
212
220
 
213
221
 
@@ -229,6 +237,7 @@ def test_deploy_with_cli_auth(
229
237
  args,
230
238
  "shared",
231
239
  None,
240
+ False,
232
241
  )
233
242
 
234
243
 
@@ -250,4 +259,49 @@ def test_deploy_with_cli_deployment_strategy(
250
259
  args,
251
260
  None,
252
261
  "rolling",
262
+ False,
263
+ )
264
+
265
+
266
+ @patch("fal.cli._utils.find_pyproject_toml", return_value="pyproject.toml")
267
+ @patch("fal.cli._utils.parse_pyproject_toml")
268
+ @patch("fal.cli.deploy._deploy_from_reference")
269
+ def test_deploy_with_cli_no_scale(
270
+ mock_deploy_ref, mock_parse_toml, mock_find_toml, mock_parse_pyproject_toml
271
+ ):
272
+ mock_parse_toml.return_value = mock_parse_pyproject_toml
273
+
274
+ args = mock_args(app_ref=("src/my_app/inference.py", "MyApp"), no_scale=True)
275
+
276
+ _deploy(args)
277
+
278
+ mock_deploy_ref.assert_called_once_with(
279
+ ("src/my_app/inference.py", "MyApp"),
280
+ None,
281
+ args,
282
+ None,
283
+ None,
284
+ True,
285
+ )
286
+
287
+
288
+ @patch("fal.cli._utils.find_pyproject_toml", return_value="pyproject.toml")
289
+ @patch("fal.cli._utils.parse_pyproject_toml")
290
+ @patch("fal.cli.deploy._deploy_from_reference")
291
+ def test_deploy_with_cli_scale(
292
+ mock_deploy_ref, mock_parse_toml, mock_find_toml, mock_parse_pyproject_toml
293
+ ):
294
+ mock_parse_toml.return_value = mock_parse_pyproject_toml
295
+
296
+ args = mock_args(app_ref=("src/my_app/inference.py", "MyApp"))
297
+
298
+ _deploy(args)
299
+
300
+ mock_deploy_ref.assert_called_once_with(
301
+ ("src/my_app/inference.py", "MyApp"),
302
+ None,
303
+ args,
304
+ None,
305
+ None,
306
+ False,
253
307
  )
@@ -2,6 +2,7 @@ from typing import Optional, Tuple
2
2
  from unittest.mock import MagicMock, patch
3
3
 
4
4
  import pytest
5
+
5
6
  from fal.cli.main import parse_args
6
7
  from fal.cli.run import _run
7
8
  from fal.files import find_project_root
@@ -1,9 +1,16 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import sys
3
4
  from functools import partial
4
5
 
5
6
  import pytest
7
+
6
8
  from fal import function
9
+ from fal.flags import GRPC_HOST
10
+ from fal.sdk import get_default_credentials
11
+
12
+ print("TARGET:", GRPC_HOST, file=sys.stderr)
13
+ print("AUTH:", get_default_credentials(), file=sys.stderr)
7
14
 
8
15
 
9
16
  @pytest.fixture(scope="function")
@@ -6,8 +6,11 @@ from pathlib import Path
6
6
  from typing import Callable
7
7
  from uuid import uuid4
8
8
 
9
- import fal
10
9
  import pytest
10
+ from pydantic import BaseModel, Field
11
+ from pydantic import __version__ as pydantic_version
12
+
13
+ import fal
11
14
  from fal import FalServerlessHost, FalServerlessKeyCredentials, local, sync_dir
12
15
  from fal.api import FalServerlessError, IsolatedFunction
13
16
  from fal.toolkit import (
@@ -21,8 +24,6 @@ from fal.toolkit import (
21
24
  )
22
25
  from fal.toolkit.file.file import CompressedFile
23
26
  from fal.toolkit.utils.download_utils import _get_git_revision_hash, _hash_url
24
- from pydantic import BaseModel, Field
25
- from pydantic import __version__ as pydantic_version
26
27
 
27
28
  EXAMPLE_FILE_URL = "https://raw.githubusercontent.com/fal-ai/fal/main/projects/fal/tests/assets/cat.png"
28
29
 
@@ -7,10 +7,17 @@ from contextlib import contextmanager, suppress
7
7
  from datetime import datetime
8
8
  from typing import Generator, List, Tuple
9
9
 
10
- import fal
11
- import fal.api as api
12
10
  import httpx
13
11
  import pytest
12
+ from fastapi import WebSocket
13
+ from httpx import HTTPStatusError
14
+ from isolate.backends.common import active_python
15
+ from openapi_fal_rest.api.applications import app_metadata
16
+ from pydantic import BaseModel
17
+ from pydantic import __version__ as pydantic_version
18
+
19
+ import fal
20
+ import fal.api as api
14
21
  from fal import apps
15
22
  from fal.app import AppClient, AppClientError
16
23
  from fal.cli.deploy import _get_user
@@ -19,12 +26,6 @@ from fal.exceptions import AppException, FieldException, RequestCancelledExcepti
19
26
  from fal.exceptions._cuda import _CUDA_OOM_MESSAGE, _CUDA_OOM_STATUS_CODE
20
27
  from fal.rest_client import REST_CLIENT
21
28
  from fal.workflows import Workflow
22
- from fastapi import WebSocket
23
- from httpx import HTTPStatusError
24
- from isolate.backends.common import active_python
25
- from openapi_fal_rest.api.applications import app_metadata
26
- from pydantic import BaseModel
27
- from pydantic import __version__ as pydantic_version
28
29
 
29
30
 
30
31
  class Input(BaseModel):
@@ -90,6 +91,26 @@ def container_addition_app(input: Input) -> Output:
90
91
  return Output(result=input.lhs + input.rhs)
91
92
 
92
93
 
94
+ @fal.function(
95
+ kind="container",
96
+ image=ContainerImage.from_dockerfile_str(
97
+ f"""FROM python:{actual_python}-slim\n# {git_revision_short_hash()}
98
+ ARG OUTPUT="built incorrectly"
99
+ ENV OUTPUT="$OUTPUT"
100
+ """,
101
+ build_args={"OUTPUT": "built with build args"},
102
+ ),
103
+ keep_alive=60,
104
+ machine_type="S",
105
+ serve=True,
106
+ max_concurrency=1,
107
+ )
108
+ def container_build_args_app() -> str:
109
+ import os
110
+
111
+ return os.environ["OUTPUT"]
112
+
113
+
93
114
  @fal.function(
94
115
  keep_alive=300,
95
116
  requirements=["fastapi", "uvicorn", "pydantic==1.10.18"],
@@ -321,6 +342,18 @@ def test_container_app():
321
342
  yield f"{user.user_id}/{app_revision}"
322
343
 
323
344
 
345
+ @pytest.fixture(scope="module")
346
+ def test_container_build_args_app():
347
+ # Create a temporary app, register it, and return the ID of it.
348
+
349
+ app_revision = container_build_args_app.host.register(
350
+ func=container_build_args_app.func,
351
+ options=container_build_args_app.options,
352
+ )
353
+ user = _get_user()
354
+ yield f"{user.user_id}/{app_revision}"
355
+
356
+
324
357
  @pytest.fixture(scope="module")
325
358
  def test_fastapi_app():
326
359
  # Create a temporary app, register it, and return the ID of it.
@@ -843,3 +876,13 @@ def test_kill_runner():
843
876
  assert len(runners) == 1
844
877
 
845
878
  client.kill_runner(runners[0].runner_id)
879
+
880
+
881
+ def test_container_app_client(test_container_app: str):
882
+ response = apps.run(test_container_app, arguments={"lhs": 1, "rhs": 2})
883
+ assert response["result"] == 3
884
+
885
+
886
+ def test_container_build_args_app_client(test_container_build_args_app: str):
887
+ response = apps.run(test_container_build_args_app, {})
888
+ assert response == "built with build args"
@@ -3,13 +3,14 @@ from __future__ import annotations
3
3
  import subprocess
4
4
  from contextlib import suppress
5
5
 
6
- import fal
7
6
  import pytest
7
+ from isolate.backends.common import active_python
8
+ from pydantic import __version__ as pydantic_version
9
+
10
+ import fal
8
11
  from fal.api import FalServerlessError, Options
9
12
  from fal.container import ContainerImage
10
13
  from fal.toolkit.file import File
11
- from isolate.backends.common import active_python
12
- from pydantic import __version__ as pydantic_version
13
14
 
14
15
  PACKAGE_NAME = "fall"
15
16
 
@@ -87,7 +88,11 @@ def test_regular_function_in_a_container_with_custom_image(isolated_client):
87
88
  @isolated_client(
88
89
  "container",
89
90
  image=ContainerImage.from_dockerfile_str(
90
- f"FROM python:{actual_python}-slim\n# {git_revision_short_hash()}"
91
+ f"""
92
+ FROM python:{actual_python}-slim
93
+ # {git_revision_short_hash()}
94
+ RUN env
95
+ """
91
96
  ),
92
97
  )
93
98
  def regular_function():
@@ -4,6 +4,7 @@ import os
4
4
  from base64 import b64encode
5
5
 
6
6
  import pytest
7
+
7
8
  from fal.toolkit.file.file import File, GoogleStorageRepository
8
9
 
9
10
 
@@ -5,11 +5,12 @@ from io import BytesIO
5
5
  from typing import Literal, overload
6
6
 
7
7
  import pytest
8
- from fal.toolkit import Image
9
8
  from PIL import Image as PILImage
10
9
  from pydantic import BaseModel, Field
11
10
  from pydantic import __version__ as pydantic_version
12
11
 
12
+ from fal.toolkit import Image
13
+
13
14
 
14
15
  @overload
15
16
  def get_image(as_bytes: Literal[False] = False) -> PILImage.Image: ...
@@ -1,6 +1,7 @@
1
1
  from unittest import mock
2
2
 
3
3
  import pytest
4
+
4
5
  from fal.toolkit.utils.retry import retry
5
6
 
6
7
 
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
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
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
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
File without changes
File without changes
File without changes