fal 1.11.0__tar.gz → 1.11.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. {fal-1.11.0/fal.egg-info → fal-1.11.1}/PKG-INFO +1 -1
  2. {fal-1.11.0 → fal-1.11.1/fal.egg-info}/PKG-INFO +1 -1
  3. {fal-1.11.0 → fal-1.11.1}/src/fal/_fal_version.py +2 -2
  4. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/_utils.py +7 -4
  5. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/utils/download_utils.py +38 -6
  6. {fal-1.11.0 → fal-1.11.1}/tests/cli/test_deploy.py +21 -0
  7. {fal-1.11.0 → fal-1.11.1}/tests/test_apps.py +17 -5
  8. {fal-1.11.0 → fal-1.11.1}/.gitignore +0 -0
  9. {fal-1.11.0 → fal-1.11.1}/Makefile +0 -0
  10. {fal-1.11.0 → fal-1.11.1}/README.md +0 -0
  11. {fal-1.11.0 → fal-1.11.1}/docs/conf.py +0 -0
  12. {fal-1.11.0 → fal-1.11.1}/docs/index.rst +0 -0
  13. {fal-1.11.0 → fal-1.11.1}/fal.egg-info/SOURCES.txt +0 -0
  14. {fal-1.11.0 → fal-1.11.1}/fal.egg-info/dependency_links.txt +0 -0
  15. {fal-1.11.0 → fal-1.11.1}/fal.egg-info/entry_points.txt +0 -0
  16. {fal-1.11.0 → fal-1.11.1}/fal.egg-info/requires.txt +0 -0
  17. {fal-1.11.0 → fal-1.11.1}/fal.egg-info/top_level.txt +0 -0
  18. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/README.md +0 -0
  19. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
  20. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
  21. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
  22. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
  23. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
  24. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
  25. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
  26. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
  27. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
  28. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
  29. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
  30. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
  31. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
  32. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
  33. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
  34. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
  35. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
  36. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
  37. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
  38. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
  39. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
  40. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
  41. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
  42. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
  43. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
  44. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
  45. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
  46. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
  47. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
  48. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
  49. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
  50. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
  51. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
  52. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
  53. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
  54. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
  55. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
  56. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
  57. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
  58. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
  59. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
  60. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
  61. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
  62. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
  63. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
  64. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
  65. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
  66. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
  67. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
  68. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
  69. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
  70. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
  71. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
  72. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
  73. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
  74. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
  75. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
  76. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
  77. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
  78. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
  79. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
  80. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
  81. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
  82. {fal-1.11.0 → fal-1.11.1}/openapi-fal-rest/pyproject.toml +0 -0
  83. {fal-1.11.0 → fal-1.11.1}/openapi_rest.config.yaml +0 -0
  84. {fal-1.11.0 → fal-1.11.1}/pyproject.toml +0 -0
  85. {fal-1.11.0 → fal-1.11.1}/setup.cfg +0 -0
  86. {fal-1.11.0 → fal-1.11.1}/src/fal/__init__.py +0 -0
  87. {fal-1.11.0 → fal-1.11.1}/src/fal/__main__.py +0 -0
  88. {fal-1.11.0 → fal-1.11.1}/src/fal/_serialization.py +0 -0
  89. {fal-1.11.0 → fal-1.11.1}/src/fal/_version.py +0 -0
  90. {fal-1.11.0 → fal-1.11.1}/src/fal/api.py +0 -0
  91. {fal-1.11.0 → fal-1.11.1}/src/fal/app.py +0 -0
  92. {fal-1.11.0 → fal-1.11.1}/src/fal/apps.py +0 -0
  93. {fal-1.11.0 → fal-1.11.1}/src/fal/auth/__init__.py +0 -0
  94. {fal-1.11.0 → fal-1.11.1}/src/fal/auth/auth0.py +0 -0
  95. {fal-1.11.0 → fal-1.11.1}/src/fal/auth/local.py +0 -0
  96. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/__init__.py +0 -0
  97. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/api.py +0 -0
  98. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/apps.py +0 -0
  99. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/auth.py +0 -0
  100. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/cli_nested_json.py +0 -0
  101. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/create.py +0 -0
  102. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/debug.py +0 -0
  103. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/deploy.py +0 -0
  104. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/doctor.py +0 -0
  105. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/keys.py +0 -0
  106. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/main.py +0 -0
  107. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/parser.py +0 -0
  108. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/profile.py +0 -0
  109. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/run.py +0 -0
  110. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/runners.py +0 -0
  111. {fal-1.11.0 → fal-1.11.1}/src/fal/cli/secrets.py +0 -0
  112. {fal-1.11.0 → fal-1.11.1}/src/fal/config.py +0 -0
  113. {fal-1.11.0 → fal-1.11.1}/src/fal/console/__init__.py +0 -0
  114. {fal-1.11.0 → fal-1.11.1}/src/fal/console/icons.py +0 -0
  115. {fal-1.11.0 → fal-1.11.1}/src/fal/console/ux.py +0 -0
  116. {fal-1.11.0 → fal-1.11.1}/src/fal/container.py +0 -0
  117. {fal-1.11.0 → fal-1.11.1}/src/fal/exceptions/__init__.py +0 -0
  118. {fal-1.11.0 → fal-1.11.1}/src/fal/exceptions/_base.py +0 -0
  119. {fal-1.11.0 → fal-1.11.1}/src/fal/exceptions/_cuda.py +0 -0
  120. {fal-1.11.0 → fal-1.11.1}/src/fal/exceptions/auth.py +0 -0
  121. {fal-1.11.0 → fal-1.11.1}/src/fal/files.py +0 -0
  122. {fal-1.11.0 → fal-1.11.1}/src/fal/flags.py +0 -0
  123. {fal-1.11.0 → fal-1.11.1}/src/fal/logging/__init__.py +0 -0
  124. {fal-1.11.0 → fal-1.11.1}/src/fal/logging/isolate.py +0 -0
  125. {fal-1.11.0 → fal-1.11.1}/src/fal/logging/style.py +0 -0
  126. {fal-1.11.0 → fal-1.11.1}/src/fal/logging/trace.py +0 -0
  127. {fal-1.11.0 → fal-1.11.1}/src/fal/logging/user.py +0 -0
  128. {fal-1.11.0 → fal-1.11.1}/src/fal/py.typed +0 -0
  129. {fal-1.11.0 → fal-1.11.1}/src/fal/rest_client.py +0 -0
  130. {fal-1.11.0 → fal-1.11.1}/src/fal/sdk.py +0 -0
  131. {fal-1.11.0 → fal-1.11.1}/src/fal/sync.py +0 -0
  132. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/__init__.py +0 -0
  133. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/exceptions.py +0 -0
  134. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/file/__init__.py +0 -0
  135. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/file/file.py +0 -0
  136. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/file/providers/fal.py +0 -0
  137. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/file/providers/gcp.py +0 -0
  138. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/file/providers/r2.py +0 -0
  139. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/file/providers/s3.py +0 -0
  140. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/file/types.py +0 -0
  141. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/image/__init__.py +0 -0
  142. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/image/image.py +0 -0
  143. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
  144. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
  145. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
  146. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
  147. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
  148. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/image/safety_checker.py +0 -0
  149. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/optimize.py +0 -0
  150. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/types.py +0 -0
  151. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/utils/__init__.py +0 -0
  152. {fal-1.11.0 → fal-1.11.1}/src/fal/toolkit/utils/retry.py +0 -0
  153. {fal-1.11.0 → fal-1.11.1}/src/fal/utils.py +0 -0
  154. {fal-1.11.0 → fal-1.11.1}/src/fal/workflows.py +0 -0
  155. {fal-1.11.0 → fal-1.11.1}/tests/__init__.py +0 -0
  156. {fal-1.11.0 → fal-1.11.1}/tests/assets/cat.png +0 -0
  157. {fal-1.11.0 → fal-1.11.1}/tests/cli/__init__.py +0 -0
  158. {fal-1.11.0 → fal-1.11.1}/tests/cli/test_apps.py +0 -0
  159. {fal-1.11.0 → fal-1.11.1}/tests/cli/test_auth.py +0 -0
  160. {fal-1.11.0 → fal-1.11.1}/tests/cli/test_keys.py +0 -0
  161. {fal-1.11.0 → fal-1.11.1}/tests/cli/test_run.py +0 -0
  162. {fal-1.11.0 → fal-1.11.1}/tests/cli/test_secrets.py +0 -0
  163. {fal-1.11.0 → fal-1.11.1}/tests/conftest.py +0 -0
  164. {fal-1.11.0 → fal-1.11.1}/tests/integration_test.py +0 -0
  165. {fal-1.11.0 → fal-1.11.1}/tests/mainify_package/__init__.py +0 -0
  166. {fal-1.11.0 → fal-1.11.1}/tests/mainify_package/impl.py +0 -0
  167. {fal-1.11.0 → fal-1.11.1}/tests/mainify_package/utils.py +0 -0
  168. {fal-1.11.0 → fal-1.11.1}/tests/mainify_target.py +0 -0
  169. {fal-1.11.0 → fal-1.11.1}/tests/test_stability.py +0 -0
  170. {fal-1.11.0 → fal-1.11.1}/tests/toolkit/file_test.py +0 -0
  171. {fal-1.11.0 → fal-1.11.1}/tests/toolkit/image_test.py +0 -0
  172. {fal-1.11.0 → fal-1.11.1}/tests/toolkit/test_types.py +0 -0
  173. {fal-1.11.0 → fal-1.11.1}/tests/toolkit/utils/retry.py +0 -0
  174. {fal-1.11.0 → fal-1.11.1}/tools/demo_script.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.11.0
3
+ Version: 1.11.1
4
4
  Summary: fal is an easy-to-use Serverless Python Framework
5
5
  Author: Features & Labels <support@fal.ai>
6
6
  Requires-Python: >=3.8
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.11.0
3
+ Version: 1.11.1
4
4
  Summary: fal is an easy-to-use Serverless Python Framework
5
5
  Author: Features & Labels <support@fal.ai>
6
6
  Requires-Python: >=3.8
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '1.11.0'
21
- __version_tuple__ = version_tuple = (1, 11, 0)
20
+ __version__ = version = '1.11.1'
21
+ __version_tuple__ = version_tuple = (1, 11, 1)
@@ -25,7 +25,7 @@ def get_app_data_from_toml(app_name):
25
25
  raise ValueError(f"App {app_name} not found in pyproject.toml")
26
26
 
27
27
  try:
28
- app_ref = app_data["ref"]
28
+ app_ref = app_data.pop("ref")
29
29
  except KeyError:
30
30
  raise ValueError(f"App {app_name} does not have a ref key in pyproject.toml")
31
31
 
@@ -33,8 +33,11 @@ def get_app_data_from_toml(app_name):
33
33
  project_root, _ = find_project_root(None)
34
34
  app_ref = str(project_root / app_ref)
35
35
 
36
- app_auth = app_data.get("auth", "private")
37
- app_deployment_strategy = app_data.get("deployment_strategy", "recreate")
38
- app_no_scale = app_data.get("no_scale", False)
36
+ app_auth = app_data.pop("auth", "private")
37
+ app_deployment_strategy = app_data.pop("deployment_strategy", "recreate")
38
+ app_no_scale = app_data.pop("no_scale", False)
39
+
40
+ if len(app_data) > 0:
41
+ raise ValueError(f"Found unexpected keys in pyproject.toml: {app_data}")
39
42
 
40
43
  return app_ref, app_auth, app_deployment_strategy, app_no_scale
@@ -126,11 +126,12 @@ def download_file(
126
126
  *,
127
127
  force: bool = False,
128
128
  request_headers: dict[str, str] | None = None,
129
+ filesize_limit: int | None = None,
129
130
  ) -> Path:
130
131
  """Downloads a file from the specified URL to the target directory.
131
132
 
132
133
  The function downloads the file from the given URL and saves it in the specified
133
- target directory.
134
+ target directory, provided it is below the given filesize limit.
134
135
 
135
136
  It also checks whether the local file already exists and whether its content length
136
137
  matches the expected content length from the remote file. If the local file already
@@ -151,6 +152,8 @@ def download_file(
151
152
  Defaults to `False`.
152
153
  request_headers: A dictionary containing additional headers to be included in
153
154
  the HTTP request. Defaults to `None`.
155
+ filesize_limit: An integer specifying the maximum downloadable size,
156
+ in megabytes. Defaults to `None`.
154
157
 
155
158
 
156
159
  Returns:
@@ -160,12 +163,22 @@ def download_file(
160
163
  ValueError: If the provided `file_name` contains a forward slash ('/').
161
164
  DownloadError: If an error occurs during the download process.
162
165
  """
166
+ ONE_MB = 1024**2
167
+
163
168
  try:
164
- file_name = _get_remote_file_properties(url, request_headers)[0]
169
+ file_name, expected_filesize = _get_remote_file_properties(url, request_headers)
165
170
  except Exception as e:
166
- print(f"GOt error: {e}")
171
+ print(f"Got error: {e}")
167
172
  raise DownloadError(f"Failed to get remote file properties for {url}") from e
168
173
 
174
+ expected_filesize_mb = expected_filesize / ONE_MB
175
+
176
+ if filesize_limit is not None and expected_filesize_mb > filesize_limit:
177
+ raise DownloadError(
178
+ f"""File to be downloaded is of size {expected_filesize_mb},
179
+ which is over the limit of {filesize_limit}"""
180
+ )
181
+
169
182
  if "/" in file_name:
170
183
  raise ValueError(f"File name '{file_name}' cannot contain a slash.")
171
184
 
@@ -194,7 +207,10 @@ def download_file(
194
207
 
195
208
  try:
196
209
  _download_file_python(
197
- url=url, target_path=target_path, request_headers=request_headers
210
+ url=url,
211
+ target_path=target_path,
212
+ request_headers=request_headers,
213
+ filesize_limit=filesize_limit,
198
214
  )
199
215
  except Exception as e:
200
216
  msg = f"Failed to download {url} to {target_path}"
@@ -207,7 +223,10 @@ def download_file(
207
223
 
208
224
 
209
225
  def _download_file_python(
210
- url: str, target_path: Path | str, request_headers: dict[str, str] | None = None
226
+ url: str,
227
+ target_path: Path | str,
228
+ request_headers: dict[str, str] | None = None,
229
+ filesize_limit: int | None = None,
211
230
  ) -> Path:
212
231
  """Download a file from a given URL and save it to a specified path using a
213
232
  Python interface.
@@ -217,6 +236,8 @@ def _download_file_python(
217
236
  target_path: The path where the downloaded file will be saved.
218
237
  request_headers: A dictionary containing additional headers to be included in
219
238
  the HTTP request. Defaults to `None`.
239
+ filesize_limit: A integer value specifying how many megabytes can be
240
+ downloaded at maximum. Defaults to `None`.
220
241
 
221
242
  Returns:
222
243
  The path where the downloaded file has been saved.
@@ -233,7 +254,10 @@ def _download_file_python(
233
254
  file_path = temp_file.name
234
255
 
235
256
  for progress, total_size in _stream_url_data_to_file(
236
- url, temp_file.name, request_headers=request_headers
257
+ url,
258
+ temp_file.name,
259
+ request_headers=request_headers,
260
+ filesize_limit=filesize_limit,
237
261
  ):
238
262
  if total_size:
239
263
  progress_msg = f"Downloading {url} ... {progress:.2%}"
@@ -261,6 +285,7 @@ def _stream_url_data_to_file(
261
285
  file_path: str,
262
286
  chunk_size_in_mb: int = 64,
263
287
  request_headers: dict[str, str] | None = None,
288
+ filesize_limit: int | None = None,
264
289
  ):
265
290
  """Download data from a URL and stream it to a file.
266
291
 
@@ -277,6 +302,8 @@ def _stream_url_data_to_file(
277
302
  Defaults to 64.
278
303
  request_headers: A dictionary containing additional headers to be included in
279
304
  the HTTP request. Defaults to `None`.
305
+ filesize_limit: An integer specifying how many megabytes can be
306
+ downloaded at maximum. Defaults to `None`.
280
307
 
281
308
  Yields:
282
309
  A tuple containing two elements:
@@ -300,6 +327,11 @@ def _stream_url_data_to_file(
300
327
  f_stream.write(data)
301
328
 
302
329
  received_size = f_stream.tell()
330
+ if filesize_limit is not None and received_size > filesize_limit:
331
+ raise DownloadError(
332
+ f"""Attempted to download more data {received_size}
333
+ than the set limit of {filesize_limit}"""
334
+ )
303
335
 
304
336
  if total_size:
305
337
  progress = received_size / total_size
@@ -26,6 +26,10 @@ def mock_parse_pyproject_toml():
26
26
  "another-app": {
27
27
  "ref": "src/another_app/inference.py::AnotherApp",
28
28
  },
29
+ "app-with-extras": {
30
+ "ref": "src/app_with_extras/inference.py::AppWithExtras",
31
+ "extra_key": "extra_value",
32
+ },
29
33
  }
30
34
  }
31
35
 
@@ -143,6 +147,23 @@ def test_deploy_with_toml_missing_ref_key(
143
147
  _deploy(args)
144
148
 
145
149
 
150
+ @patch("fal.cli._utils.find_pyproject_toml", return_value="pyproject.toml")
151
+ @patch("fal.cli._utils.parse_pyproject_toml")
152
+ @patch("fal.cli.deploy._deploy_from_reference")
153
+ def test_deploy_with_toml_extra_keys_in_toml(
154
+ mock_deploy_ref, mock_parse_toml, mock_find_toml, mock_parse_pyproject_toml
155
+ ):
156
+ mock_parse_toml.return_value = mock_parse_pyproject_toml
157
+
158
+ args = mock_args(app_ref=("app-with-extras", None))
159
+
160
+ with pytest.raises(
161
+ ValueError,
162
+ match="Found unexpected keys in pyproject.toml: {'extra_key': 'extra_value'}",
163
+ ):
164
+ _deploy(args)
165
+
166
+
146
167
  @patch("fal.cli._utils.find_pyproject_toml", return_value=None)
147
168
  def test_deploy_with_toml_file_not_found(mock_find_toml):
148
169
  args = mock_args(app_ref=("my-app", None))
@@ -484,13 +484,19 @@ def test_stateful_app_client(test_stateful_app: str):
484
484
  assert response["result"] == 0
485
485
 
486
486
 
487
- @pytest.mark.flaky(max_runs=3)
488
487
  def test_app_cancellation(test_app: str, test_cancellable_app: str):
489
488
  request_handle = apps.submit(
490
489
  test_cancellable_app, arguments={"lhs": 1, "rhs": 2, "wait_time": 10}
491
490
  )
492
- # enough time for it to start
493
- time.sleep(8)
491
+
492
+ while True:
493
+ status = request_handle.status()
494
+ time.sleep(0.5)
495
+ if isinstance(status, apps.InProgress):
496
+ # The app is running
497
+ break
498
+
499
+ # cancel the request
494
500
  request_handle.cancel()
495
501
 
496
502
  # should still finish successfully and return 499
@@ -503,8 +509,14 @@ def test_app_cancellation(test_app: str, test_cancellable_app: str):
503
509
  test_app, arguments={"lhs": 1, "rhs": 2, "wait_time": 10}
504
510
  )
505
511
 
506
- # enough time for it to start
507
- time.sleep(8)
512
+ while True:
513
+ status = request_handle.status()
514
+ time.sleep(0.5)
515
+ if isinstance(status, apps.InProgress):
516
+ # The app is running
517
+ break
518
+
519
+ # cancel the request
508
520
  request_handle.cancel()
509
521
 
510
522
  response = request_handle.get()
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
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