fal 1.46.0__tar.gz → 1.46.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 (201) hide show
  1. {fal-1.46.0/fal.egg-info → fal-1.46.2}/PKG-INFO +2 -1
  2. {fal-1.46.0 → fal-1.46.2/fal.egg-info}/PKG-INFO +2 -1
  3. {fal-1.46.0 → fal-1.46.2}/fal.egg-info/requires.txt +1 -0
  4. {fal-1.46.0 → fal-1.46.2}/pyproject.toml +1 -0
  5. {fal-1.46.0 → fal-1.46.2}/src/fal/_fal_version.py +2 -2
  6. {fal-1.46.0 → fal-1.46.2}/src/fal/app.py +74 -4
  7. {fal-1.46.0 → fal-1.46.2}/src/fal/distributed/utils.py +3 -3
  8. {fal-1.46.0 → fal-1.46.2}/src/fal/distributed/worker.py +21 -6
  9. {fal-1.46.0 → fal-1.46.2}/src/fal/file_sync.py +7 -6
  10. {fal-1.46.0 → fal-1.46.2}/tests/unit/test_app.py +25 -5
  11. {fal-1.46.0 → fal-1.46.2}/.gitignore +0 -0
  12. {fal-1.46.0 → fal-1.46.2}/Makefile +0 -0
  13. {fal-1.46.0 → fal-1.46.2}/README.md +0 -0
  14. {fal-1.46.0 → fal-1.46.2}/docs/conf.py +0 -0
  15. {fal-1.46.0 → fal-1.46.2}/docs/index.rst +0 -0
  16. {fal-1.46.0 → fal-1.46.2}/fal.egg-info/SOURCES.txt +0 -0
  17. {fal-1.46.0 → fal-1.46.2}/fal.egg-info/dependency_links.txt +0 -0
  18. {fal-1.46.0 → fal-1.46.2}/fal.egg-info/entry_points.txt +0 -0
  19. {fal-1.46.0 → fal-1.46.2}/fal.egg-info/top_level.txt +0 -0
  20. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/README.md +0 -0
  21. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
  22. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
  23. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
  24. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
  25. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
  26. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
  27. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
  28. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
  29. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
  30. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
  31. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
  32. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
  33. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
  34. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
  35. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
  36. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
  37. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
  38. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
  39. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
  40. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
  41. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
  42. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
  43. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
  44. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
  45. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
  46. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
  47. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
  48. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
  49. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
  50. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
  51. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
  52. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
  53. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
  54. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
  55. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
  56. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
  57. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
  58. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
  59. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
  60. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
  61. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
  62. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
  63. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
  64. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
  65. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
  66. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
  67. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
  68. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
  69. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
  70. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
  71. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
  72. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
  73. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
  74. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
  75. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
  76. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
  77. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
  78. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
  79. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
  80. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
  81. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
  82. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
  83. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
  84. {fal-1.46.0 → fal-1.46.2}/openapi-fal-rest/pyproject.toml +0 -0
  85. {fal-1.46.0 → fal-1.46.2}/openapi_rest.config.yaml +0 -0
  86. {fal-1.46.0 → fal-1.46.2}/setup.cfg +0 -0
  87. {fal-1.46.0 → fal-1.46.2}/src/fal/__init__.py +0 -0
  88. {fal-1.46.0 → fal-1.46.2}/src/fal/__main__.py +0 -0
  89. {fal-1.46.0 → fal-1.46.2}/src/fal/_serialization.py +0 -0
  90. {fal-1.46.0 → fal-1.46.2}/src/fal/_version.py +0 -0
  91. {fal-1.46.0 → fal-1.46.2}/src/fal/api/__init__.py +0 -0
  92. {fal-1.46.0 → fal-1.46.2}/src/fal/api/api.py +0 -0
  93. {fal-1.46.0 → fal-1.46.2}/src/fal/api/apps.py +0 -0
  94. {fal-1.46.0 → fal-1.46.2}/src/fal/api/client.py +0 -0
  95. {fal-1.46.0 → fal-1.46.2}/src/fal/api/deploy.py +0 -0
  96. {fal-1.46.0 → fal-1.46.2}/src/fal/api/runners.py +0 -0
  97. {fal-1.46.0 → fal-1.46.2}/src/fal/apps.py +0 -0
  98. {fal-1.46.0 → fal-1.46.2}/src/fal/auth/__init__.py +0 -0
  99. {fal-1.46.0 → fal-1.46.2}/src/fal/auth/auth0.py +0 -0
  100. {fal-1.46.0 → fal-1.46.2}/src/fal/auth/local.py +0 -0
  101. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/__init__.py +0 -0
  102. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/_utils.py +0 -0
  103. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/api.py +0 -0
  104. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/apps.py +0 -0
  105. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/auth.py +0 -0
  106. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/cli_nested_json.py +0 -0
  107. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/create.py +0 -0
  108. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/debug.py +0 -0
  109. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/deploy.py +0 -0
  110. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/doctor.py +0 -0
  111. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/files.py +0 -0
  112. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/keys.py +0 -0
  113. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/main.py +0 -0
  114. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/parser.py +0 -0
  115. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/profile.py +0 -0
  116. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/queue.py +0 -0
  117. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/run.py +0 -0
  118. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/runners.py +0 -0
  119. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/secrets.py +0 -0
  120. {fal-1.46.0 → fal-1.46.2}/src/fal/cli/teams.py +0 -0
  121. {fal-1.46.0 → fal-1.46.2}/src/fal/config.py +0 -0
  122. {fal-1.46.0 → fal-1.46.2}/src/fal/console/__init__.py +0 -0
  123. {fal-1.46.0 → fal-1.46.2}/src/fal/console/icons.py +0 -0
  124. {fal-1.46.0 → fal-1.46.2}/src/fal/console/ux.py +0 -0
  125. {fal-1.46.0 → fal-1.46.2}/src/fal/container.py +0 -0
  126. {fal-1.46.0 → fal-1.46.2}/src/fal/distributed/__init__.py +0 -0
  127. {fal-1.46.0 → fal-1.46.2}/src/fal/exceptions/__init__.py +0 -0
  128. {fal-1.46.0 → fal-1.46.2}/src/fal/exceptions/_base.py +0 -0
  129. {fal-1.46.0 → fal-1.46.2}/src/fal/exceptions/_cuda.py +0 -0
  130. {fal-1.46.0 → fal-1.46.2}/src/fal/exceptions/auth.py +0 -0
  131. {fal-1.46.0 → fal-1.46.2}/src/fal/files.py +0 -0
  132. {fal-1.46.0 → fal-1.46.2}/src/fal/flags.py +0 -0
  133. {fal-1.46.0 → fal-1.46.2}/src/fal/logging/__init__.py +0 -0
  134. {fal-1.46.0 → fal-1.46.2}/src/fal/logging/isolate.py +0 -0
  135. {fal-1.46.0 → fal-1.46.2}/src/fal/logging/style.py +0 -0
  136. {fal-1.46.0 → fal-1.46.2}/src/fal/logging/trace.py +0 -0
  137. {fal-1.46.0 → fal-1.46.2}/src/fal/project.py +0 -0
  138. {fal-1.46.0 → fal-1.46.2}/src/fal/py.typed +0 -0
  139. {fal-1.46.0 → fal-1.46.2}/src/fal/rest_client.py +0 -0
  140. {fal-1.46.0 → fal-1.46.2}/src/fal/sdk.py +0 -0
  141. {fal-1.46.0 → fal-1.46.2}/src/fal/sync.py +0 -0
  142. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/__init__.py +0 -0
  143. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/audio/__init__.py +0 -0
  144. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/audio/audio.py +0 -0
  145. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/exceptions.py +0 -0
  146. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/file/__init__.py +0 -0
  147. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/file/file.py +0 -0
  148. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/file/providers/fal.py +0 -0
  149. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/file/providers/gcp.py +0 -0
  150. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/file/providers/r2.py +0 -0
  151. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/file/providers/s3.py +0 -0
  152. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/file/types.py +0 -0
  153. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/image/__init__.py +0 -0
  154. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/image/image.py +0 -0
  155. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
  156. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
  157. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
  158. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
  159. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
  160. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/image/safety_checker.py +0 -0
  161. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/kv.py +0 -0
  162. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/optimize.py +0 -0
  163. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/types.py +0 -0
  164. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/utils/__init__.py +0 -0
  165. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/utils/download_utils.py +0 -0
  166. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/utils/endpoint.py +0 -0
  167. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/utils/retry.py +0 -0
  168. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/utils/setup_utils.py +0 -0
  169. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/video/__init__.py +0 -0
  170. {fal-1.46.0 → fal-1.46.2}/src/fal/toolkit/video/video.py +0 -0
  171. {fal-1.46.0 → fal-1.46.2}/src/fal/utils.py +0 -0
  172. {fal-1.46.0 → fal-1.46.2}/src/fal/workflows.py +0 -0
  173. {fal-1.46.0 → fal-1.46.2}/tests/__init__.py +0 -0
  174. {fal-1.46.0 → fal-1.46.2}/tests/assets/cat.png +0 -0
  175. {fal-1.46.0 → fal-1.46.2}/tests/cli/__init__.py +0 -0
  176. {fal-1.46.0 → fal-1.46.2}/tests/cli/test_apps.py +0 -0
  177. {fal-1.46.0 → fal-1.46.2}/tests/cli/test_auth.py +0 -0
  178. {fal-1.46.0 → fal-1.46.2}/tests/cli/test_deploy.py +0 -0
  179. {fal-1.46.0 → fal-1.46.2}/tests/cli/test_keys.py +0 -0
  180. {fal-1.46.0 → fal-1.46.2}/tests/cli/test_run.py +0 -0
  181. {fal-1.46.0 → fal-1.46.2}/tests/cli/test_secrets.py +0 -0
  182. {fal-1.46.0 → fal-1.46.2}/tests/conftest.py +0 -0
  183. {fal-1.46.0 → fal-1.46.2}/tests/integration_test.py +0 -0
  184. {fal-1.46.0 → fal-1.46.2}/tests/mainify_package/__init__.py +0 -0
  185. {fal-1.46.0 → fal-1.46.2}/tests/mainify_package/impl.py +0 -0
  186. {fal-1.46.0 → fal-1.46.2}/tests/mainify_package/utils.py +0 -0
  187. {fal-1.46.0 → fal-1.46.2}/tests/mainify_target.py +0 -0
  188. {fal-1.46.0 → fal-1.46.2}/tests/test_apps.py +0 -0
  189. {fal-1.46.0 → fal-1.46.2}/tests/test_file_sync.py +0 -0
  190. {fal-1.46.0 → fal-1.46.2}/tests/test_files.py +0 -0
  191. {fal-1.46.0 → fal-1.46.2}/tests/test_kv.py +0 -0
  192. {fal-1.46.0 → fal-1.46.2}/tests/test_stability.py +0 -0
  193. {fal-1.46.0 → fal-1.46.2}/tests/toolkit/file/providers/test_fal_retry.py +0 -0
  194. {fal-1.46.0 → fal-1.46.2}/tests/toolkit/file_test.py +0 -0
  195. {fal-1.46.0 → fal-1.46.2}/tests/toolkit/image_test.py +0 -0
  196. {fal-1.46.0 → fal-1.46.2}/tests/toolkit/test_types.py +0 -0
  197. {fal-1.46.0 → fal-1.46.2}/tests/toolkit/utils/retry.py +0 -0
  198. {fal-1.46.0 → fal-1.46.2}/tests/unit/distributed/test_integration.py +0 -0
  199. {fal-1.46.0 → fal-1.46.2}/tests/unit/distributed/test_utils.py +0 -0
  200. {fal-1.46.0 → fal-1.46.2}/tests/unit/distributed/test_worker.py +0 -0
  201. {fal-1.46.0 → fal-1.46.2}/tools/demo_script.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.46.0
3
+ Version: 1.46.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
@@ -53,6 +53,7 @@ Requires-Dist: pytest-xdist; extra == "test"
53
53
  Requires-Dist: pytest-timeout; extra == "test"
54
54
  Requires-Dist: flaky; extra == "test"
55
55
  Requires-Dist: boto3; extra == "test"
56
+ Requires-Dist: numpy; extra == "test"
56
57
  Provides-Extra: dev
57
58
  Requires-Dist: fal[docs,test]; extra == "dev"
58
59
  Requires-Dist: openapi-python-client<1,>=0.14.1; extra == "dev"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.46.0
3
+ Version: 1.46.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
@@ -53,6 +53,7 @@ Requires-Dist: pytest-xdist; extra == "test"
53
53
  Requires-Dist: pytest-timeout; extra == "test"
54
54
  Requires-Dist: flaky; extra == "test"
55
55
  Requires-Dist: boto3; extra == "test"
56
+ Requires-Dist: numpy; extra == "test"
56
57
  Provides-Extra: dev
57
58
  Requires-Dist: fal[docs,test]; extra == "dev"
58
59
  Requires-Dist: openapi-python-client<1,>=0.14.1; extra == "dev"
@@ -54,3 +54,4 @@ pytest-xdist
54
54
  pytest-timeout
55
55
  flaky
56
56
  boto3
57
+ numpy
@@ -77,6 +77,7 @@ test = [
77
77
  "pytest-timeout",
78
78
  "flaky",
79
79
  "boto3",
80
+ "numpy",
80
81
  ]
81
82
  dev = [
82
83
  "fal[docs,test]",
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '1.46.0'
32
- __version_tuple__ = version_tuple = (1, 46, 0)
31
+ __version__ = version = '1.46.2'
32
+ __version_tuple__ = version_tuple = (1, 46, 2)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -6,11 +6,13 @@ import json
6
6
  import os
7
7
  import queue
8
8
  import re
9
+ import sys
9
10
  import threading
10
11
  import time
11
12
  import typing
12
13
  from contextlib import asynccontextmanager, contextmanager
13
14
  from dataclasses import dataclass
15
+ from pathlib import Path
14
16
  from typing import Any, Callable, ClassVar, Optional, TypeVar
15
17
 
16
18
  import fastapi
@@ -109,6 +111,10 @@ async def _set_logger_labels(
109
111
  def wrap_app(cls: type[App], **kwargs) -> IsolatedFunction:
110
112
  include_modules_from(cls)
111
113
 
114
+ host = kwargs.get("host", None)
115
+ if host:
116
+ cls.local_file_path = host.local_file_path
117
+
112
118
  def initialize_and_serve():
113
119
  app = cls()
114
120
  app.serve()
@@ -306,12 +312,61 @@ def _print_python_packages() -> None:
306
312
  print("[debug] Python packages installed:", ", ".join(packages))
307
313
 
308
314
 
309
- def _include_app_files_path():
310
- import sys # noqa: PLC0415
315
+ def _include_app_files_path(
316
+ local_file_path: str | None, app_files_context_dir: str | None
317
+ ):
318
+ base_cloud_dir = Path("/app")
319
+ if local_file_path is None:
320
+ return
321
+
322
+ # In case of container apps, the /app directory is not created by default
323
+ # so we need to check if it exists before proceeding
324
+ if not base_cloud_dir.exists():
325
+ return
326
+
327
+ base_path = Path(local_file_path).resolve()
328
+ if base_path.is_dir():
329
+ original_script_dir = base_path
330
+ else:
331
+ original_script_dir = base_path.parent
332
+
333
+ if app_files_context_dir:
334
+ context_path = Path(app_files_context_dir)
335
+ if context_path.is_absolute():
336
+ final_script_dir = context_path.resolve()
337
+ else:
338
+ final_script_dir = (original_script_dir / context_path).resolve()
339
+
340
+ # relative path between the original script dir
341
+ # and where the app_files_context_dir is targetting
342
+ relative_path = os.path.relpath(original_script_dir, final_script_dir)
343
+ # cloud final_path based on the `/app` base dir,
344
+ final_path = base_cloud_dir / Path(relative_path)
345
+ else:
346
+ # if no app_files_context_dir is provided, the base directory is the root
347
+ final_path = base_cloud_dir
348
+
349
+ # Create the final path if it doesn't exist
350
+ # This is for cases when fal app is not in root
351
+ # and its parent directory is not in app_files
352
+ # Which means that the relative path to app won't be created by default
353
+ final_path.mkdir(parents=True, exist_ok=True)
311
354
 
312
355
  # Add local files deployment path to sys.path so imports
313
356
  # work correctly in the isolate agent
314
- sys.path.append("/app_files")
357
+ # Append the final path to sys.path first so that the
358
+ # relative directory is resolved first in case of conflicts
359
+ sys.path.append(str(final_path))
360
+
361
+ # Add the base cloud dir path to sys.path so that
362
+ # the app can access the files in the top level directory
363
+ # This is for cases when fal app is not in root,
364
+ # and user wants to access the files without using relative imports
365
+ sys.path.append(str(base_cloud_dir))
366
+
367
+ # Change the current working directory to the path of the app
368
+ # so that the app can access the files in the current directory
369
+ os.chdir(str(final_path))
315
370
 
316
371
 
317
372
  class App(BaseServable):
@@ -341,6 +396,7 @@ class App(BaseServable):
341
396
  max_multiplexing: ClassVar[Optional[int]] = None
342
397
  kind: ClassVar[Optional[str]] = None
343
398
  image: ClassVar[Optional[ContainerImage]] = None
399
+ local_file_path: ClassVar[Optional[str]] = None
344
400
 
345
401
  isolate_channel: async_grpc.Channel | None = None
346
402
 
@@ -363,6 +419,10 @@ class App(BaseServable):
363
419
 
364
420
  if cls.app_files_context_dir is not None:
365
421
  cls.host_kwargs["app_files_context_dir"] = cls.app_files_context_dir
422
+ if not cls.app_files:
423
+ raise ValueError(
424
+ "app_files_context_dir is only supported when app_files is provided"
425
+ )
366
426
 
367
427
  if cls.min_concurrency is not None:
368
428
  cls.host_kwargs["min_concurrency"] = cls.min_concurrency
@@ -381,12 +441,17 @@ class App(BaseServable):
381
441
 
382
442
  if cls.kind is not None:
383
443
  cls.host_kwargs["kind"] = cls.kind
444
+ if cls.kind == "container" and cls.app_files:
445
+ raise ValueError("app_files is not supported for container apps.")
384
446
 
385
447
  if cls.image is not None:
386
448
  cls.host_kwargs["image"] = cls.image
387
449
 
388
450
  cls.app_name = getattr(cls, "app_name") or app_name
389
451
 
452
+ if kwargs.get("kind") and cls.app_files:
453
+ raise ValueError("app_files is not supported for container apps.")
454
+
390
455
  if cls.__init__ is not App.__init__:
391
456
  raise ValueError(
392
457
  "App classes should not override __init__ directly. "
@@ -418,7 +483,12 @@ class App(BaseServable):
418
483
 
419
484
  @asynccontextmanager
420
485
  async def lifespan(self, app: fastapi.FastAPI):
421
- _include_app_files_path()
486
+ # We want to not do any directory changes for container apps,
487
+ # since we don't have explicit checks to see the kind of app
488
+ # We check for app_files here and check kind and app_files earlier
489
+ # to ensure that container apps don't have app_files
490
+ if self.app_files:
491
+ _include_app_files_path(self.local_file_path, self.app_files_context_dir)
422
492
  _print_python_packages()
423
493
  await _call_any_fn(self.setup)
424
494
  try:
@@ -8,7 +8,7 @@ import warnings
8
8
  from collections.abc import Callable
9
9
  from io import BytesIO
10
10
  from pathlib import Path
11
- from typing import TYPE_CHECKING, Any, Optional, Union
11
+ from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union
12
12
 
13
13
  if TYPE_CHECKING:
14
14
  import torch.multiprocessing as mp
@@ -271,8 +271,8 @@ def wrap_distributed_worker(
271
271
  master_port: int,
272
272
  timeout: int,
273
273
  cwd: Optional[Union[str, Path]],
274
- args: tuple[Any],
275
- kwargs: dict[str, Any],
274
+ args: Tuple[Any],
275
+ kwargs: Dict[str, Any],
276
276
  ) -> None:
277
277
  """
278
278
  Worker function for distributed training or inference.
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import inspect
3
5
  import os
@@ -9,6 +11,7 @@ import traceback
9
11
  import warnings
10
12
  from collections.abc import AsyncIterator, Callable, Coroutine
11
13
  from concurrent.futures import Future
14
+ from functools import partial
12
15
  from pathlib import Path
13
16
  from typing import TYPE_CHECKING, Any, Optional, Union
14
17
 
@@ -70,7 +73,7 @@ class DistributedWorker:
70
73
  # Public API
71
74
 
72
75
  @property
73
- def device(self) -> "torch.device":
76
+ def device(self) -> torch.device:
74
77
  """
75
78
  :return: The device for the current worker.
76
79
  """
@@ -162,6 +165,16 @@ class DistributedWorker:
162
165
  self.loop.call_soon_threadsafe(self.loop.stop)
163
166
  self.thread.join(timeout=timeout)
164
167
 
168
+ async def _run_sync_in_executor(
169
+ self,
170
+ func: Callable[..., Any],
171
+ *args: Any,
172
+ **kwargs: Any,
173
+ ) -> Any:
174
+ """Run a synchronous function in the executor."""
175
+ loop = asyncio.get_running_loop()
176
+ return await loop.run_in_executor(None, partial(func, *args, **kwargs))
177
+
165
178
  def run_in_worker(
166
179
  self,
167
180
  func: Callable[..., Any],
@@ -174,7 +187,9 @@ class DistributedWorker:
174
187
  if inspect.iscoroutinefunction(func):
175
188
  coro = func(*args, **kwargs)
176
189
  else:
177
- coro = asyncio.to_thread(func, *args, **kwargs)
190
+ # Using in place of asyncio.to_thread
191
+ # since it's not available in Python 3.8
192
+ coro = self._run_sync_in_executor(func, *args, **kwargs)
178
193
 
179
194
  return self.submit(coro)
180
195
 
@@ -206,8 +221,8 @@ class DistributedRunner:
206
221
  A class to launch and manage distributed workers.
207
222
  """
208
223
 
209
- zmq_socket: Optional["Socket[Any]"]
210
- context: Optional["mp.ProcessContext"]
224
+ zmq_socket: Optional[Socket[Any]]
225
+ context: Optional[mp.ProcessContext]
211
226
  keepalive_timer: Optional[KeepAliveTimer]
212
227
 
213
228
  def __init__(
@@ -296,7 +311,7 @@ class DistributedRunner:
296
311
  f"Distributed processes are not running. Errors: {self.gather_errors()}"
297
312
  )
298
313
 
299
- def get_zmq_socket(self) -> "Socket[Any]":
314
+ def get_zmq_socket(self) -> Socket[Any]:
300
315
  """
301
316
  Returns a ZeroMQ socket of the specified type.
302
317
  :param socket_type: The type of the ZeroMQ socket.
@@ -750,7 +765,7 @@ class DistributedRunner:
750
765
  assert rank == b"0", "Expected response from worker with rank 0"
751
766
  return distributed_deserialize(response)
752
767
 
753
- async def __aenter__(self) -> "DistributedRunner":
768
+ async def __aenter__(self) -> DistributedRunner:
754
769
  """
755
770
  Enter the context manager.
756
771
  :return: The DistributedRunner instance.
@@ -10,6 +10,7 @@ from typing import Dict, List, Optional, Tuple
10
10
  import httpx
11
11
  from rich.tree import Tree
12
12
 
13
+ import fal.flags as flags
13
14
  from fal._version import version_tuple
14
15
  from fal.console import console
15
16
  from fal.console.icons import CROSS_ICON
@@ -25,7 +26,7 @@ DEFAULT_CONCURRENCY_UPLOADS = 10
25
26
 
26
27
 
27
28
  def print_path_tree(file_paths):
28
- tree = Tree("/app_files")
29
+ tree = Tree("/app")
29
30
 
30
31
  nodes = {"": tree}
31
32
 
@@ -272,8 +273,8 @@ class FileSync:
272
273
  filtered_files: List[FileMetadata] = []
273
274
  for metadata in files:
274
275
  if self._matches_patterns(metadata.relative_path, files_ignore):
275
- # TODO: hide behind DEBUG flag?
276
- console.print(f"Ignoring file: {metadata.relative_path}")
276
+ if flags.DEBUG:
277
+ console.print(f"Ignoring file: {metadata.relative_path}")
277
278
  else:
278
279
  filtered_files.append(metadata)
279
280
 
@@ -344,9 +345,9 @@ class FileSync:
344
345
  else:
345
346
  uploaded_files.append((metadata, future.result()))
346
347
 
347
- # TODO: hide behind DEBUG flag?
348
- console.print("File Structure:")
349
- print_path_tree([m.relative_path for m in unique_files])
348
+ if flags.DEBUG:
349
+ console.print("File Structure:")
350
+ print_path_tree([m.relative_path for m in unique_files])
350
351
 
351
352
  return unique_files, errors
352
353
 
@@ -15,9 +15,6 @@ def test_app_classvars_propagate_to_host_kwargs():
15
15
  class VarsApp(App):
16
16
  request_timeout = 11
17
17
  startup_timeout = 22
18
- app_files = ["a.py", "b.py"]
19
- app_files_ignore = [r"\\.venv/"]
20
- app_files_context_dir = "."
21
18
  min_concurrency = 2
22
19
  max_concurrency = 3
23
20
  concurrency_buffer = 4
@@ -28,6 +25,31 @@ def test_app_classvars_propagate_to_host_kwargs():
28
25
  "FROM python:3.10-slim",
29
26
  )
30
27
 
28
+ hk = VarsApp.host_kwargs
29
+ assert hk["request_timeout"] == 11
30
+ assert hk["startup_timeout"] == 22
31
+ assert hk["min_concurrency"] == 2
32
+ assert hk["max_concurrency"] == 3
33
+ assert hk["concurrency_buffer"] == 4
34
+ assert hk["concurrency_buffer_perc"] == 50
35
+ assert hk["max_multiplexing"] == 7
36
+ assert hk["kind"] == "container"
37
+ assert isinstance(hk["image"], ContainerImage)
38
+
39
+
40
+ def test_app_files_classvars_propagate_to_host_kwargs():
41
+ class VarsApp(App):
42
+ request_timeout = 11
43
+ startup_timeout = 22
44
+ app_files = ["a.py", "b.py"]
45
+ app_files_ignore = [r"\\.venv/"]
46
+ app_files_context_dir = "."
47
+ min_concurrency = 2
48
+ max_concurrency = 3
49
+ concurrency_buffer = 4
50
+ concurrency_buffer_perc = 50
51
+ max_multiplexing = 7
52
+
31
53
  hk = VarsApp.host_kwargs
32
54
  assert hk["request_timeout"] == 11
33
55
  assert hk["startup_timeout"] == 22
@@ -39,8 +61,6 @@ def test_app_classvars_propagate_to_host_kwargs():
39
61
  assert hk["concurrency_buffer"] == 4
40
62
  assert hk["concurrency_buffer_perc"] == 50
41
63
  assert hk["max_multiplexing"] == 7
42
- assert hk["kind"] == "container"
43
- assert isinstance(hk["image"], ContainerImage)
44
64
 
45
65
 
46
66
  def test_app_kwargs_merge_into_host_kwargs_and_override_defaults():
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
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