fal 1.45.0__tar.gz → 1.45.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 (190) hide show
  1. {fal-1.45.0/fal.egg-info → fal-1.45.2}/PKG-INFO +1 -1
  2. {fal-1.45.0 → fal-1.45.2/fal.egg-info}/PKG-INFO +1 -1
  3. {fal-1.45.0 → fal-1.45.2}/fal.egg-info/SOURCES.txt +1 -0
  4. {fal-1.45.0 → fal-1.45.2}/src/fal/_fal_version.py +2 -2
  5. {fal-1.45.0 → fal-1.45.2}/src/fal/app.py +10 -1
  6. {fal-1.45.0 → fal-1.45.2}/src/fal/file_sync.py +13 -19
  7. fal-1.45.2/tests/unit/test_app.py +71 -0
  8. {fal-1.45.0 → fal-1.45.2}/.gitignore +0 -0
  9. {fal-1.45.0 → fal-1.45.2}/Makefile +0 -0
  10. {fal-1.45.0 → fal-1.45.2}/README.md +0 -0
  11. {fal-1.45.0 → fal-1.45.2}/docs/conf.py +0 -0
  12. {fal-1.45.0 → fal-1.45.2}/docs/index.rst +0 -0
  13. {fal-1.45.0 → fal-1.45.2}/fal.egg-info/dependency_links.txt +0 -0
  14. {fal-1.45.0 → fal-1.45.2}/fal.egg-info/entry_points.txt +0 -0
  15. {fal-1.45.0 → fal-1.45.2}/fal.egg-info/requires.txt +0 -0
  16. {fal-1.45.0 → fal-1.45.2}/fal.egg-info/top_level.txt +0 -0
  17. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/README.md +0 -0
  18. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
  19. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
  20. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
  21. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
  22. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
  23. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
  24. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/__init__.py +0 -0
  25. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/create_workflow.py +0 -0
  26. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/delete_workflow.py +0 -0
  27. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/get_workflow.py +0 -0
  28. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/list_user_workflows.py +0 -0
  29. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/comfy/update_workflow.py +0 -0
  30. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
  31. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
  32. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
  33. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/users/__init__.py +0 -0
  34. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/users/get_current_user.py +0 -0
  35. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
  36. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_workflow.py +0 -0
  37. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow.py +0 -0
  38. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow.py +0 -0
  39. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/list_user_workflows.py +0 -0
  40. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/api/workflows/update_workflow.py +0 -0
  41. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
  42. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
  43. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
  44. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
  45. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
  46. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_detail.py +0 -0
  47. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_item.py +0 -0
  48. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema.py +0 -0
  49. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_extra_data.py +0 -0
  50. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs.py +0 -0
  51. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_fal_inputs_dev_info.py +0 -0
  52. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/comfy_workflow_schema_prompt.py +0 -0
  53. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/current_user.py +0 -0
  54. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
  55. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
  56. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
  57. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
  58. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/page_comfy_workflow_item.py +0 -0
  59. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
  60. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/team_role.py +0 -0
  61. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow.py +0 -0
  62. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/typed_comfy_workflow_update.py +0 -0
  63. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
  64. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow_update.py +0 -0
  65. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/user_member.py +0 -0
  66. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
  67. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
  68. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_metadata.py +0 -0
  69. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
  70. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
  71. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
  72. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents.py +0 -0
  73. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
  74. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
  75. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
  76. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
  77. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
  78. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
  79. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
  80. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
  81. {fal-1.45.0 → fal-1.45.2}/openapi-fal-rest/pyproject.toml +0 -0
  82. {fal-1.45.0 → fal-1.45.2}/openapi_rest.config.yaml +0 -0
  83. {fal-1.45.0 → fal-1.45.2}/pyproject.toml +0 -0
  84. {fal-1.45.0 → fal-1.45.2}/setup.cfg +0 -0
  85. {fal-1.45.0 → fal-1.45.2}/src/fal/__init__.py +0 -0
  86. {fal-1.45.0 → fal-1.45.2}/src/fal/__main__.py +0 -0
  87. {fal-1.45.0 → fal-1.45.2}/src/fal/_serialization.py +0 -0
  88. {fal-1.45.0 → fal-1.45.2}/src/fal/_version.py +0 -0
  89. {fal-1.45.0 → fal-1.45.2}/src/fal/api.py +0 -0
  90. {fal-1.45.0 → fal-1.45.2}/src/fal/apps.py +0 -0
  91. {fal-1.45.0 → fal-1.45.2}/src/fal/auth/__init__.py +0 -0
  92. {fal-1.45.0 → fal-1.45.2}/src/fal/auth/auth0.py +0 -0
  93. {fal-1.45.0 → fal-1.45.2}/src/fal/auth/local.py +0 -0
  94. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/__init__.py +0 -0
  95. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/_utils.py +0 -0
  96. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/api.py +0 -0
  97. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/apps.py +0 -0
  98. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/auth.py +0 -0
  99. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/cli_nested_json.py +0 -0
  100. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/create.py +0 -0
  101. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/debug.py +0 -0
  102. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/deploy.py +0 -0
  103. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/doctor.py +0 -0
  104. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/files.py +0 -0
  105. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/keys.py +0 -0
  106. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/main.py +0 -0
  107. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/parser.py +0 -0
  108. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/profile.py +0 -0
  109. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/queue.py +0 -0
  110. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/run.py +0 -0
  111. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/runners.py +0 -0
  112. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/secrets.py +0 -0
  113. {fal-1.45.0 → fal-1.45.2}/src/fal/cli/teams.py +0 -0
  114. {fal-1.45.0 → fal-1.45.2}/src/fal/config.py +0 -0
  115. {fal-1.45.0 → fal-1.45.2}/src/fal/console/__init__.py +0 -0
  116. {fal-1.45.0 → fal-1.45.2}/src/fal/console/icons.py +0 -0
  117. {fal-1.45.0 → fal-1.45.2}/src/fal/console/ux.py +0 -0
  118. {fal-1.45.0 → fal-1.45.2}/src/fal/container.py +0 -0
  119. {fal-1.45.0 → fal-1.45.2}/src/fal/exceptions/__init__.py +0 -0
  120. {fal-1.45.0 → fal-1.45.2}/src/fal/exceptions/_base.py +0 -0
  121. {fal-1.45.0 → fal-1.45.2}/src/fal/exceptions/_cuda.py +0 -0
  122. {fal-1.45.0 → fal-1.45.2}/src/fal/exceptions/auth.py +0 -0
  123. {fal-1.45.0 → fal-1.45.2}/src/fal/files.py +0 -0
  124. {fal-1.45.0 → fal-1.45.2}/src/fal/flags.py +0 -0
  125. {fal-1.45.0 → fal-1.45.2}/src/fal/logging/__init__.py +0 -0
  126. {fal-1.45.0 → fal-1.45.2}/src/fal/logging/isolate.py +0 -0
  127. {fal-1.45.0 → fal-1.45.2}/src/fal/logging/style.py +0 -0
  128. {fal-1.45.0 → fal-1.45.2}/src/fal/logging/trace.py +0 -0
  129. {fal-1.45.0 → fal-1.45.2}/src/fal/project.py +0 -0
  130. {fal-1.45.0 → fal-1.45.2}/src/fal/py.typed +0 -0
  131. {fal-1.45.0 → fal-1.45.2}/src/fal/rest_client.py +0 -0
  132. {fal-1.45.0 → fal-1.45.2}/src/fal/sdk.py +0 -0
  133. {fal-1.45.0 → fal-1.45.2}/src/fal/sync.py +0 -0
  134. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/__init__.py +0 -0
  135. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/audio/__init__.py +0 -0
  136. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/audio/audio.py +0 -0
  137. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/exceptions.py +0 -0
  138. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/file/__init__.py +0 -0
  139. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/file/file.py +0 -0
  140. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/file/providers/fal.py +0 -0
  141. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/file/providers/gcp.py +0 -0
  142. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/file/providers/r2.py +0 -0
  143. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/file/providers/s3.py +0 -0
  144. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/file/types.py +0 -0
  145. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/image/__init__.py +0 -0
  146. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/image/image.py +0 -0
  147. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/image/nsfw_filter/__init__.py +0 -0
  148. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/image/nsfw_filter/env.py +0 -0
  149. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/image/nsfw_filter/inference.py +0 -0
  150. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/image/nsfw_filter/model.py +0 -0
  151. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/image/nsfw_filter/requirements.txt +0 -0
  152. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/image/safety_checker.py +0 -0
  153. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/kv.py +0 -0
  154. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/optimize.py +0 -0
  155. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/types.py +0 -0
  156. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/utils/__init__.py +0 -0
  157. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/utils/download_utils.py +0 -0
  158. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/utils/endpoint.py +0 -0
  159. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/utils/retry.py +0 -0
  160. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/utils/setup_utils.py +0 -0
  161. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/video/__init__.py +0 -0
  162. {fal-1.45.0 → fal-1.45.2}/src/fal/toolkit/video/video.py +0 -0
  163. {fal-1.45.0 → fal-1.45.2}/src/fal/utils.py +0 -0
  164. {fal-1.45.0 → fal-1.45.2}/src/fal/workflows.py +0 -0
  165. {fal-1.45.0 → fal-1.45.2}/tests/__init__.py +0 -0
  166. {fal-1.45.0 → fal-1.45.2}/tests/assets/cat.png +0 -0
  167. {fal-1.45.0 → fal-1.45.2}/tests/cli/__init__.py +0 -0
  168. {fal-1.45.0 → fal-1.45.2}/tests/cli/test_apps.py +0 -0
  169. {fal-1.45.0 → fal-1.45.2}/tests/cli/test_auth.py +0 -0
  170. {fal-1.45.0 → fal-1.45.2}/tests/cli/test_deploy.py +0 -0
  171. {fal-1.45.0 → fal-1.45.2}/tests/cli/test_keys.py +0 -0
  172. {fal-1.45.0 → fal-1.45.2}/tests/cli/test_run.py +0 -0
  173. {fal-1.45.0 → fal-1.45.2}/tests/cli/test_secrets.py +0 -0
  174. {fal-1.45.0 → fal-1.45.2}/tests/conftest.py +0 -0
  175. {fal-1.45.0 → fal-1.45.2}/tests/integration_test.py +0 -0
  176. {fal-1.45.0 → fal-1.45.2}/tests/mainify_package/__init__.py +0 -0
  177. {fal-1.45.0 → fal-1.45.2}/tests/mainify_package/impl.py +0 -0
  178. {fal-1.45.0 → fal-1.45.2}/tests/mainify_package/utils.py +0 -0
  179. {fal-1.45.0 → fal-1.45.2}/tests/mainify_target.py +0 -0
  180. {fal-1.45.0 → fal-1.45.2}/tests/test_apps.py +0 -0
  181. {fal-1.45.0 → fal-1.45.2}/tests/test_file_sync.py +0 -0
  182. {fal-1.45.0 → fal-1.45.2}/tests/test_files.py +0 -0
  183. {fal-1.45.0 → fal-1.45.2}/tests/test_kv.py +0 -0
  184. {fal-1.45.0 → fal-1.45.2}/tests/test_stability.py +0 -0
  185. {fal-1.45.0 → fal-1.45.2}/tests/toolkit/file/providers/test_fal_retry.py +0 -0
  186. {fal-1.45.0 → fal-1.45.2}/tests/toolkit/file_test.py +0 -0
  187. {fal-1.45.0 → fal-1.45.2}/tests/toolkit/image_test.py +0 -0
  188. {fal-1.45.0 → fal-1.45.2}/tests/toolkit/test_types.py +0 -0
  189. {fal-1.45.0 → fal-1.45.2}/tests/toolkit/utils/retry.py +0 -0
  190. {fal-1.45.0 → fal-1.45.2}/tools/demo_script.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.45.0
3
+ Version: 1.45.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
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.45.0
3
+ Version: 1.45.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
@@ -184,4 +184,5 @@ tests/toolkit/image_test.py
184
184
  tests/toolkit/test_types.py
185
185
  tests/toolkit/file/providers/test_fal_retry.py
186
186
  tests/toolkit/utils/retry.py
187
+ tests/unit/test_app.py
187
188
  tools/demo_script.py
@@ -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.45.0'
32
- __version_tuple__ = version_tuple = (1, 45, 0)
31
+ __version__ = version = '1.45.2'
32
+ __version_tuple__ = version_tuple = (1, 45, 2)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -27,6 +27,7 @@ from fal.api import (
27
27
  from fal.api import (
28
28
  function as fal_function,
29
29
  )
30
+ from fal.container import ContainerImage
30
31
  from fal.exceptions import FalServerlessException, RequestCancelledException
31
32
  from fal.logging import get_logger
32
33
  from fal.sdk import AuthModeLiteral
@@ -338,6 +339,8 @@ class App(BaseServable):
338
339
  concurrency_buffer: ClassVar[Optional[int]] = None
339
340
  concurrency_buffer_perc: ClassVar[Optional[int]] = None
340
341
  max_multiplexing: ClassVar[Optional[int]] = None
342
+ kind: ClassVar[Optional[str]] = None
343
+ image: ClassVar[Optional[ContainerImage]] = None
341
344
 
342
345
  isolate_channel: async_grpc.Channel | None = None
343
346
 
@@ -376,7 +379,13 @@ class App(BaseServable):
376
379
  if cls.max_multiplexing is not None:
377
380
  cls.host_kwargs["max_multiplexing"] = cls.max_multiplexing
378
381
 
379
- cls.app_name = getattr(cls, "app_name", app_name)
382
+ if cls.kind is not None:
383
+ cls.host_kwargs["kind"] = cls.kind
384
+
385
+ if cls.image is not None:
386
+ cls.host_kwargs["image"] = cls.image
387
+
388
+ cls.app_name = getattr(cls, "app_name") or app_name
380
389
 
381
390
  if cls.__init__ is not App.__init__:
382
391
  raise ValueError(
@@ -266,9 +266,6 @@ class FileSync:
266
266
  files_context_dir: Optional[str] = None,
267
267
  ) -> Tuple[List[FileMetadata], List[AppFileUploadException]]:
268
268
  files = self.collect_files(paths, files_context_dir)
269
- existing_hashes: List[FileMetadata] = []
270
- uploaded_files: List[Tuple[FileMetadata, str]] = []
271
- errors: List[AppFileUploadException] = []
272
269
 
273
270
  # Filter out ignored files
274
271
  if files_ignore:
@@ -302,24 +299,23 @@ class FileSync:
302
299
  if rel_path not in seen_relative_paths:
303
300
  seen_relative_paths.add(rel_path)
304
301
 
305
- files_to_check: List[FileMetadata] = []
306
- for metadata in unique_files:
307
- files_to_check.append(metadata)
302
+ if not unique_files:
303
+ return [], []
308
304
 
309
- if not files_to_check:
310
- return existing_hashes, errors
311
-
312
- hashes_to_check = [metadata.hash for metadata in files_to_check]
313
- missing_hashes = self.check_hashes_on_server(hashes_to_check)
305
+ hashes_to_check = list({metadata.hash for metadata in unique_files})
306
+ missing_hashes = set(self.check_hashes_on_server(hashes_to_check))
314
307
 
315
308
  # Categorize based on server response
316
309
  files_to_upload: List[FileMetadata] = []
317
- for file in files_to_check:
318
- if file.hash not in missing_hashes:
319
- existing_hashes.append(file)
320
- else:
310
+ for file in unique_files:
311
+ if file.hash in missing_hashes:
312
+ # No longer missing as we are uploading it
313
+ # Removing it avoids duplicate uploads
314
+ missing_hashes.remove(file.hash)
321
315
  files_to_upload.append(file)
322
316
 
317
+ uploaded_files: List[Tuple[FileMetadata, str]] = []
318
+ errors: List[AppFileUploadException] = []
323
319
  # Upload missing files in parallel with bounded concurrency
324
320
  if files_to_upload:
325
321
  # Embed it here to be able to pass it to the executor
@@ -348,13 +344,11 @@ class FileSync:
348
344
  else:
349
345
  uploaded_files.append((metadata, future.result()))
350
346
 
351
- all_files = existing_hashes + [file for file, _ in uploaded_files]
352
-
353
347
  # TODO: hide behind DEBUG flag?
354
348
  console.print("File Structure:")
355
- print_path_tree([m.relative_path for m in all_files])
349
+ print_path_tree([m.relative_path for m in unique_files])
356
350
 
357
- return all_files, errors
351
+ return unique_files, errors
358
352
 
359
353
  def close(self):
360
354
  """Close HTTP client."""
@@ -0,0 +1,71 @@
1
+ from __future__ import annotations
2
+
3
+ from fal import App
4
+ from fal.container import ContainerImage
5
+
6
+
7
+ def test_app_default_app_name_is_generated_from_class_name():
8
+ class MyCustomApp(App):
9
+ pass
10
+
11
+ assert MyCustomApp.app_name == "my-custom-app"
12
+
13
+
14
+ def test_app_classvars_propagate_to_host_kwargs():
15
+ class VarsApp(App):
16
+ request_timeout = 11
17
+ startup_timeout = 22
18
+ app_files = ["a.py", "b.py"]
19
+ app_files_ignore = [r"\\.venv/"]
20
+ app_files_context_dir = "."
21
+ min_concurrency = 2
22
+ max_concurrency = 3
23
+ concurrency_buffer = 4
24
+ concurrency_buffer_perc = 50
25
+ max_multiplexing = 7
26
+ kind = "container"
27
+ image = ContainerImage.from_dockerfile_str(
28
+ "FROM python:3.10-slim",
29
+ )
30
+
31
+ hk = VarsApp.host_kwargs
32
+ assert hk["request_timeout"] == 11
33
+ assert hk["startup_timeout"] == 22
34
+ assert hk["app_files"] == ["a.py", "b.py"]
35
+ assert hk["app_files_ignore"] == [r"\\.venv/"]
36
+ assert hk["app_files_context_dir"] == "."
37
+ assert hk["min_concurrency"] == 2
38
+ assert hk["max_concurrency"] == 3
39
+ assert hk["concurrency_buffer"] == 4
40
+ assert hk["concurrency_buffer_perc"] == 50
41
+ assert hk["max_multiplexing"] == 7
42
+ assert hk["kind"] == "container"
43
+ assert isinstance(hk["image"], ContainerImage)
44
+
45
+
46
+ def test_app_kwargs_merge_into_host_kwargs_and_override_defaults():
47
+ class KwargsApp(App, keep_alive=123, resolver="pip", _scheduler="nomad"):
48
+ pass
49
+
50
+ hk = KwargsApp.host_kwargs
51
+ assert hk["keep_alive"] == 123
52
+ assert hk["resolver"] == "pip"
53
+ assert hk["_scheduler"] == "nomad"
54
+ # default keys should still exist
55
+ assert "_scheduler_options" in hk
56
+
57
+
58
+ def test_non_host_classvars_do_not_leak_into_host_kwargs():
59
+ class LeakCheckApp(App):
60
+ requirements = ["fastapi"]
61
+ local_python_modules = ["."]
62
+ machine_type = "M"
63
+ num_gpus = 1
64
+ app_auth = "private"
65
+
66
+ hk = LeakCheckApp.host_kwargs
67
+ assert "requirements" not in hk
68
+ assert "local_python_modules" not in hk
69
+ assert "machine_type" not in hk
70
+ assert "num_gpus" not in hk
71
+ assert "app_auth" not in hk
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