fal 1.0.4__tar.gz → 1.0.5__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 (128) hide show
  1. {fal-1.0.4 → fal-1.0.5}/PKG-INFO +4 -4
  2. {fal-1.0.4 → fal-1.0.5}/fal.egg-info/PKG-INFO +4 -4
  3. {fal-1.0.4 → fal-1.0.5}/fal.egg-info/SOURCES.txt +1 -0
  4. {fal-1.0.4 → fal-1.0.5}/fal.egg-info/requires.txt +3 -3
  5. {fal-1.0.4 → fal-1.0.5}/pyproject.toml +3 -3
  6. {fal-1.0.4 → fal-1.0.5}/src/fal/__init__.py +2 -0
  7. {fal-1.0.4 → fal-1.0.5}/src/fal/_fal_version.py +2 -2
  8. {fal-1.0.4 → fal-1.0.5}/src/fal/api.py +54 -4
  9. {fal-1.0.4 → fal-1.0.5}/src/fal/app.py +2 -1
  10. fal-1.0.5/src/fal/container.py +19 -0
  11. {fal-1.0.4 → fal-1.0.5}/tests/test_apps.py +32 -0
  12. {fal-1.0.4 → fal-1.0.5}/tests/test_stability.py +37 -1
  13. {fal-1.0.4 → fal-1.0.5}/.gitignore +0 -0
  14. {fal-1.0.4 → fal-1.0.5}/README.md +0 -0
  15. {fal-1.0.4 → fal-1.0.5}/fal.egg-info/dependency_links.txt +0 -0
  16. {fal-1.0.4 → fal-1.0.5}/fal.egg-info/entry_points.txt +0 -0
  17. {fal-1.0.4 → fal-1.0.5}/fal.egg-info/top_level.txt +0 -0
  18. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/README.md +0 -0
  19. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/__init__.py +0 -0
  20. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/api/__init__.py +0 -0
  21. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/api/applications/__init__.py +0 -0
  22. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/api/applications/app_metadata.py +0 -0
  23. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/api/billing/__init__.py +0 -0
  24. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/api/billing/get_user_details.py +0 -0
  25. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/api/files/__init__.py +0 -0
  26. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/api/files/check_dir_hash.py +0 -0
  27. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/api/files/upload_local_file.py +0 -0
  28. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/api/workflows/__init__.py +0 -0
  29. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/api/workflows/create_or_update_workflow_workflows_post.py +0 -0
  30. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/api/workflows/delete_workflow_workflows_user_id_workflow_name_delete.py +0 -0
  31. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/api/workflows/execute_workflow_workflows_user_id_workflow_name_post.py +0 -0
  32. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflow_workflows_user_id_workflow_name_get.py +0 -0
  33. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/api/workflows/get_workflows_workflows_get.py +0 -0
  34. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/client.py +0 -0
  35. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/errors.py +0 -0
  36. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/__init__.py +0 -0
  37. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/app_metadata_response_app_metadata.py +0 -0
  38. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/body_upload_local_file.py +0 -0
  39. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/customer_details.py +0 -0
  40. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/execute_workflow_workflows_user_id_workflow_name_post_json_body_type_0.py +0 -0
  41. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/execute_workflow_workflows_user_id_workflow_name_post_response_200_type_0.py +0 -0
  42. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/hash_check.py +0 -0
  43. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/http_validation_error.py +0 -0
  44. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/lock_reason.py +0 -0
  45. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/page_workflow_item.py +0 -0
  46. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/typed_workflow.py +0 -0
  47. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/validation_error.py +0 -0
  48. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents.py +0 -0
  49. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_nodes.py +0 -0
  50. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_contents_output.py +0 -0
  51. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail.py +0 -0
  52. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_detail_contents_type_0.py +0 -0
  53. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_item.py +0 -0
  54. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_node.py +0 -0
  55. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_node_type.py +0 -0
  56. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema.py +0 -0
  57. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_input.py +0 -0
  58. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/models/workflow_schema_output.py +0 -0
  59. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/py.typed +0 -0
  60. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/openapi_fal_rest/types.py +0 -0
  61. {fal-1.0.4 → fal-1.0.5}/openapi-fal-rest/pyproject.toml +0 -0
  62. {fal-1.0.4 → fal-1.0.5}/openapi_rest.config.yaml +0 -0
  63. {fal-1.0.4 → fal-1.0.5}/setup.cfg +0 -0
  64. {fal-1.0.4 → fal-1.0.5}/src/fal/__main__.py +0 -0
  65. {fal-1.0.4 → fal-1.0.5}/src/fal/_serialization.py +0 -0
  66. {fal-1.0.4 → fal-1.0.5}/src/fal/_version.py +0 -0
  67. {fal-1.0.4 → fal-1.0.5}/src/fal/apps.py +0 -0
  68. {fal-1.0.4 → fal-1.0.5}/src/fal/auth/__init__.py +0 -0
  69. {fal-1.0.4 → fal-1.0.5}/src/fal/auth/auth0.py +0 -0
  70. {fal-1.0.4 → fal-1.0.5}/src/fal/auth/local.py +0 -0
  71. {fal-1.0.4 → fal-1.0.5}/src/fal/cli/__init__.py +0 -0
  72. {fal-1.0.4 → fal-1.0.5}/src/fal/cli/apps.py +0 -0
  73. {fal-1.0.4 → fal-1.0.5}/src/fal/cli/auth.py +0 -0
  74. {fal-1.0.4 → fal-1.0.5}/src/fal/cli/debug.py +0 -0
  75. {fal-1.0.4 → fal-1.0.5}/src/fal/cli/deploy.py +0 -0
  76. {fal-1.0.4 → fal-1.0.5}/src/fal/cli/keys.py +0 -0
  77. {fal-1.0.4 → fal-1.0.5}/src/fal/cli/main.py +0 -0
  78. {fal-1.0.4 → fal-1.0.5}/src/fal/cli/parser.py +0 -0
  79. {fal-1.0.4 → fal-1.0.5}/src/fal/cli/run.py +0 -0
  80. {fal-1.0.4 → fal-1.0.5}/src/fal/cli/secrets.py +0 -0
  81. {fal-1.0.4 → fal-1.0.5}/src/fal/console/__init__.py +0 -0
  82. {fal-1.0.4 → fal-1.0.5}/src/fal/console/icons.py +0 -0
  83. {fal-1.0.4 → fal-1.0.5}/src/fal/console/ux.py +0 -0
  84. {fal-1.0.4 → fal-1.0.5}/src/fal/exceptions/__init__.py +0 -0
  85. {fal-1.0.4 → fal-1.0.5}/src/fal/exceptions/_base.py +0 -0
  86. {fal-1.0.4 → fal-1.0.5}/src/fal/exceptions/auth.py +0 -0
  87. {fal-1.0.4 → fal-1.0.5}/src/fal/flags.py +0 -0
  88. {fal-1.0.4 → fal-1.0.5}/src/fal/logging/__init__.py +0 -0
  89. {fal-1.0.4 → fal-1.0.5}/src/fal/logging/isolate.py +0 -0
  90. {fal-1.0.4 → fal-1.0.5}/src/fal/logging/style.py +0 -0
  91. {fal-1.0.4 → fal-1.0.5}/src/fal/logging/trace.py +0 -0
  92. {fal-1.0.4 → fal-1.0.5}/src/fal/logging/user.py +0 -0
  93. {fal-1.0.4 → fal-1.0.5}/src/fal/py.typed +0 -0
  94. {fal-1.0.4 → fal-1.0.5}/src/fal/rest_client.py +0 -0
  95. {fal-1.0.4 → fal-1.0.5}/src/fal/sdk.py +0 -0
  96. {fal-1.0.4 → fal-1.0.5}/src/fal/sync.py +0 -0
  97. {fal-1.0.4 → fal-1.0.5}/src/fal/toolkit/__init__.py +0 -0
  98. {fal-1.0.4 → fal-1.0.5}/src/fal/toolkit/exceptions.py +0 -0
  99. {fal-1.0.4 → fal-1.0.5}/src/fal/toolkit/file/__init__.py +0 -0
  100. {fal-1.0.4 → fal-1.0.5}/src/fal/toolkit/file/file.py +0 -0
  101. {fal-1.0.4 → fal-1.0.5}/src/fal/toolkit/file/providers/fal.py +0 -0
  102. {fal-1.0.4 → fal-1.0.5}/src/fal/toolkit/file/providers/gcp.py +0 -0
  103. {fal-1.0.4 → fal-1.0.5}/src/fal/toolkit/file/providers/r2.py +0 -0
  104. {fal-1.0.4 → fal-1.0.5}/src/fal/toolkit/file/types.py +0 -0
  105. {fal-1.0.4 → fal-1.0.5}/src/fal/toolkit/image/__init__.py +0 -0
  106. {fal-1.0.4 → fal-1.0.5}/src/fal/toolkit/image/image.py +0 -0
  107. {fal-1.0.4 → fal-1.0.5}/src/fal/toolkit/optimize.py +0 -0
  108. {fal-1.0.4 → fal-1.0.5}/src/fal/toolkit/utils/__init__.py +0 -0
  109. {fal-1.0.4 → fal-1.0.5}/src/fal/toolkit/utils/download_utils.py +0 -0
  110. {fal-1.0.4 → fal-1.0.5}/src/fal/utils.py +0 -0
  111. {fal-1.0.4 → fal-1.0.5}/src/fal/workflows.py +0 -0
  112. {fal-1.0.4 → fal-1.0.5}/tests/__init__.py +0 -0
  113. {fal-1.0.4 → fal-1.0.5}/tests/cli/__init__.py +0 -0
  114. {fal-1.0.4 → fal-1.0.5}/tests/cli/test_apps.py +0 -0
  115. {fal-1.0.4 → fal-1.0.5}/tests/cli/test_auth.py +0 -0
  116. {fal-1.0.4 → fal-1.0.5}/tests/cli/test_deploy.py +0 -0
  117. {fal-1.0.4 → fal-1.0.5}/tests/cli/test_keys.py +0 -0
  118. {fal-1.0.4 → fal-1.0.5}/tests/cli/test_run.py +0 -0
  119. {fal-1.0.4 → fal-1.0.5}/tests/cli/test_secrets.py +0 -0
  120. {fal-1.0.4 → fal-1.0.5}/tests/conftest.py +0 -0
  121. {fal-1.0.4 → fal-1.0.5}/tests/integration_test.py +0 -0
  122. {fal-1.0.4 → fal-1.0.5}/tests/mainify_package/__init__.py +0 -0
  123. {fal-1.0.4 → fal-1.0.5}/tests/mainify_package/impl.py +0 -0
  124. {fal-1.0.4 → fal-1.0.5}/tests/mainify_package/utils.py +0 -0
  125. {fal-1.0.4 → fal-1.0.5}/tests/mainify_target.py +0 -0
  126. {fal-1.0.4 → fal-1.0.5}/tests/toolkit/file_test.py +0 -0
  127. {fal-1.0.4 → fal-1.0.5}/tests/toolkit/image_test.py +0 -0
  128. {fal-1.0.4 → fal-1.0.5}/tools/demo_script.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fal
3
- Version: 1.0.4
3
+ Version: 1.0.5
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
7
7
  Description-Content-Type: text/markdown
8
- Requires-Dist: isolate[build]<1.0,>=0.12.3
9
- Requires-Dist: isolate-proto==0.4.0
10
- Requires-Dist: grpcio<2,>=1.50.0
8
+ Requires-Dist: isolate[build]<1.14.0,>=0.13.0
9
+ Requires-Dist: isolate-proto==0.5.1
10
+ Requires-Dist: grpcio==1.64.0
11
11
  Requires-Dist: dill==0.3.7
12
12
  Requires-Dist: cloudpickle==3.0.0
13
13
  Requires-Dist: typing-extensions<5,>=4.7.1
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fal
3
- Version: 1.0.4
3
+ Version: 1.0.5
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
7
7
  Description-Content-Type: text/markdown
8
- Requires-Dist: isolate[build]<1.0,>=0.12.3
9
- Requires-Dist: isolate-proto==0.4.0
10
- Requires-Dist: grpcio<2,>=1.50.0
8
+ Requires-Dist: isolate[build]<1.14.0,>=0.13.0
9
+ Requires-Dist: isolate-proto==0.5.1
10
+ Requires-Dist: grpcio==1.64.0
11
11
  Requires-Dist: dill==0.3.7
12
12
  Requires-Dist: cloudpickle==3.0.0
13
13
  Requires-Dist: typing-extensions<5,>=4.7.1
@@ -60,6 +60,7 @@ src/fal/_version.py
60
60
  src/fal/api.py
61
61
  src/fal/app.py
62
62
  src/fal/apps.py
63
+ src/fal/container.py
63
64
  src/fal/flags.py
64
65
  src/fal/py.typed
65
66
  src/fal/rest_client.py
@@ -1,6 +1,6 @@
1
- isolate[build]<1.0,>=0.12.3
2
- isolate-proto==0.4.0
3
- grpcio<2,>=1.50.0
1
+ isolate[build]<1.14.0,>=0.13.0
2
+ isolate-proto==0.5.1
3
+ grpcio==1.64.0
4
4
  dill==0.3.7
5
5
  cloudpickle==3.0.0
6
6
  typing-extensions<5,>=4.7.1
@@ -22,9 +22,9 @@ authors = [{ name = "Features & Labels <support@fal.ai>"}]
22
22
  readme = "README.md"
23
23
  requires-python = ">=3.8"
24
24
  dependencies = [
25
- "isolate[build]>=0.12.3,<1.0",
26
- "isolate-proto==0.4.0",
27
- "grpcio>=1.50.0,<2",
25
+ "isolate[build]>=0.13.0,<1.14.0",
26
+ "isolate-proto==0.5.1",
27
+ "grpcio==1.64.0",
28
28
  "dill==0.3.7",
29
29
  "cloudpickle==3.0.0",
30
30
  "typing-extensions>=4.7.1,<5",
@@ -4,6 +4,7 @@ from fal import apps # noqa: F401
4
4
  from fal.api import FalServerlessHost, LocalHost, cached, function
5
5
  from fal.api import function as isolated # noqa: F401
6
6
  from fal.app import App, endpoint, realtime, wrap_app # noqa: F401
7
+ from fal.container import ContainerImage
7
8
  from fal.sdk import FalServerlessKeyCredentials
8
9
  from fal.sync import sync_dir
9
10
 
@@ -26,4 +27,5 @@ __all__ = [
26
27
  "sync_dir",
27
28
  "__version__",
28
29
  "version_tuple",
30
+ "ContainerImage",
29
31
  ]
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.0.4'
16
- __version_tuple__ = version_tuple = (1, 0, 4)
15
+ __version__ = version = '1.0.5'
16
+ __version_tuple__ = version_tuple = (1, 0, 5)
@@ -42,6 +42,7 @@ from typing_extensions import Concatenate, ParamSpec
42
42
 
43
43
  import fal.flags as flags
44
44
  from fal._serialization import include_modules_from, patch_pickle
45
+ from fal.container import ContainerImage
45
46
  from fal.exceptions import FalServerlessException
46
47
  from fal.logging.isolate import IsolateLogPrinter
47
48
  from fal.sdk import (
@@ -523,9 +524,12 @@ class Options:
523
524
  pip_requirements = self.environment.setdefault("requirements", [])
524
525
  elif kind == "conda":
525
526
  pip_requirements = self.environment.setdefault("pip", [])
527
+ elif kind == "container":
528
+ return None
526
529
  else:
527
530
  raise FalServerlessError(
528
- "Only conda and virtualenv is supported as environment options"
531
+ "Only {conda, virtualenv, container} "
532
+ "are supported as environment options."
529
533
  )
530
534
 
531
535
  # Already has these.
@@ -743,8 +747,55 @@ def function(
743
747
  _scheduler: str | None = None,
744
748
  ) -> Callable[
745
749
  [Callable[Concatenate[ArgsT], ReturnT]], ServedIsolatedFunction[ArgsT, ReturnT]
746
- ]:
747
- ...
750
+ ]: ...
751
+
752
+
753
+ @overload
754
+ def function(
755
+ kind: Literal["container"],
756
+ *,
757
+ image: ContainerImage | None = None,
758
+ # Common options
759
+ host: FalServerlessHost = _DEFAULT_HOST,
760
+ serve: Literal[False] = False,
761
+ exposed_port: int | None = None,
762
+ max_concurrency: int | None = None,
763
+ # FalServerlessHost options
764
+ metadata: dict[str, Any] | None = None,
765
+ machine_type: str = FAL_SERVERLESS_DEFAULT_MACHINE_TYPE,
766
+ keep_alive: int = FAL_SERVERLESS_DEFAULT_KEEP_ALIVE,
767
+ max_multiplexing: int = FAL_SERVERLESS_DEFAULT_MAX_MULTIPLEXING,
768
+ min_concurrency: int = FAL_SERVERLESS_DEFAULT_MIN_CONCURRENCY,
769
+ setup_function: Callable[..., None] | None = None,
770
+ _base_image: str | None = None,
771
+ _scheduler: str | None = None,
772
+ ) -> Callable[
773
+ [Callable[Concatenate[ArgsT], ReturnT]], IsolatedFunction[ArgsT, ReturnT]
774
+ ]: ...
775
+
776
+
777
+ @overload
778
+ def function(
779
+ kind: Literal["container"],
780
+ *,
781
+ image: ContainerImage | None = None,
782
+ # Common options
783
+ host: FalServerlessHost = _DEFAULT_HOST,
784
+ serve: Literal[True],
785
+ exposed_port: int | None = None,
786
+ max_concurrency: int | None = None,
787
+ # FalServerlessHost options
788
+ metadata: dict[str, Any] | None = None,
789
+ machine_type: str = FAL_SERVERLESS_DEFAULT_MACHINE_TYPE,
790
+ keep_alive: int = FAL_SERVERLESS_DEFAULT_KEEP_ALIVE,
791
+ max_multiplexing: int = FAL_SERVERLESS_DEFAULT_MAX_MULTIPLEXING,
792
+ min_concurrency: int = FAL_SERVERLESS_DEFAULT_MIN_CONCURRENCY,
793
+ setup_function: Callable[..., None] | None = None,
794
+ _base_image: str | None = None,
795
+ _scheduler: str | None = None,
796
+ ) -> Callable[
797
+ [Callable[Concatenate[ArgsT], ReturnT]], ServedIsolatedFunction[ArgsT, ReturnT]
798
+ ]: ...
748
799
 
749
800
 
750
801
  # implementation
@@ -1121,4 +1172,3 @@ class Server(uvicorn.Server):
1121
1172
 
1122
1173
  def install_signal_handlers(self) -> None:
1123
1174
  pass
1124
-
@@ -47,8 +47,9 @@ def wrap_app(cls: type[App], **kwargs) -> fal.api.IsolatedFunction:
47
47
  routes = app.collect_routes()
48
48
  realtime_app = any(route.is_websocket for route in routes)
49
49
 
50
+ kind = kwargs.pop("kind", "virtualenv")
50
51
  wrapper = fal.api.function(
51
- "virtualenv",
52
+ kind,
52
53
  requirements=cls.requirements,
53
54
  machine_type=cls.machine_type,
54
55
  **cls.host_kwargs,
@@ -0,0 +1,19 @@
1
+ class ContainerImage:
2
+ """ContainerImage represents a Docker image that can be built
3
+ from a Dockerfile.
4
+ """
5
+
6
+ _known_keys = {"dockerfile_str", "build_env", "build_args"}
7
+
8
+ @classmethod
9
+ def from_dockerfile_str(cls, text: str, **kwargs):
10
+ # Check for unknown keys and return them as a dict.
11
+ return dict(
12
+ dockerfile_str=text,
13
+ **{k: v for k, v in kwargs.items() if k in cls._known_keys},
14
+ )
15
+
16
+ @classmethod
17
+ def from_dockerfile(cls, path: str, **kwargs):
18
+ with open(path) as fobj:
19
+ return cls.from_dockerfile_str(fobj.read(), **kwargs)
@@ -8,6 +8,7 @@ import fal.api as api
8
8
  import httpx
9
9
  import pytest
10
10
  from fal import apps
11
+ from fal.container import ContainerImage
11
12
  from fal.rest_client import REST_CLIENT
12
13
  from fal.workflows import Workflow
13
14
  from fastapi import WebSocket
@@ -49,6 +50,23 @@ def addition_app(input: Input) -> Output:
49
50
 
50
51
  nomad_addition_app = addition_app.on(_scheduler="nomad")
51
52
 
53
+ @fal.function(
54
+ kind="container",
55
+ image=ContainerImage.from_dockerfile_str("FROM python:3.11"),
56
+ keep_alive=60,
57
+ machine_type="S",
58
+ serve=True,
59
+ max_concurrency=1,
60
+ )
61
+ def container_addition_app(input: Input) -> Output:
62
+ print("starting...")
63
+ for _ in range(input.wait_time):
64
+ print("sleeping...")
65
+ time.sleep(1)
66
+
67
+ return Output(result=input.lhs + input.rhs)
68
+
69
+
52
70
  @fal.function(
53
71
  keep_alive=300,
54
72
  requirements=["fastapi", "uvicorn", "pydantic==1.10.12"],
@@ -201,6 +219,20 @@ def test_nomad_app():
201
219
  yield f"{user_id}/{app_revision}"
202
220
 
203
221
 
222
+ @pytest.mark.xfail(reason="The support needs to be deployed. See https://github.com/fal-ai/isolate-cloud/pull/1809")
223
+ @pytest.fixture(scope="module")
224
+ def test_container_app():
225
+ # Create a temporary app, register it, and return the ID of it.
226
+
227
+ from fal.cli.deploy import _get_user_id
228
+
229
+ app_revision = container_addition_app.host.register(
230
+ func=container_addition_app.func,
231
+ options=container_addition_app.options,
232
+ )
233
+ user_id = _get_user_id()
234
+ yield f"{user_id}/{app_revision}"
235
+
204
236
  @pytest.fixture(scope="module")
205
237
  def test_fastapi_app():
206
238
  # Create a temporary app, register it, and return the ID of it.
@@ -5,12 +5,14 @@ from contextlib import suppress
5
5
  import fal
6
6
  import pytest
7
7
  from fal.api import FalServerlessError, Options
8
+ from fal.container import ContainerImage
8
9
  from fal.toolkit.file.file import File
9
10
  from pydantic import __version__ as pydantic_version
10
11
 
11
- PACKAGE_NAME = "fal"
12
+ PACKAGE_NAME = "fall"
12
13
 
13
14
 
15
+ @pytest.mark.xfail(reason="Temporary mismatch due to grpc version updates. Ping @efiop")
14
16
  def test_missing_dependencies_nested_server_error(isolated_client):
15
17
  @isolated_client()
16
18
  def test1():
@@ -54,6 +56,40 @@ def test_regular_function_on_nomad(isolated_client):
54
56
 
55
57
  assert mult(5, 2) == 10
56
58
 
59
+ @pytest.mark.xfail(reason="The support needs to be deployed. See https://github.com/fal-ai/isolate-cloud/pull/1809")
60
+ def test_regular_function_in_a_container(isolated_client):
61
+ @isolated_client("container")
62
+ def regular_function():
63
+ return 42
64
+
65
+ assert regular_function() == 42
66
+
67
+ @isolated_client("container")
68
+ def mult(a, b):
69
+ return a * b
70
+
71
+ assert mult(5, 2) == 10
72
+
73
+ @pytest.mark.xfail(reason="The support needs to be deployed. See https://github.com/fal-ai/isolate-cloud/pull/1809")
74
+ def test_regular_function_in_a_container_with_custom_image(isolated_client):
75
+ @isolated_client(
76
+ "container",
77
+ image=ContainerImage.from_dockerfile_str("FROM python:3.9"),
78
+ )
79
+ def regular_function():
80
+ return 42
81
+
82
+ assert regular_function() == 42
83
+
84
+ @isolated_client(
85
+ "container",
86
+ image=ContainerImage.from_dockerfile_str("FROM python:3.9"),
87
+ )
88
+ def mult(a, b):
89
+ return a * b
90
+
91
+ assert mult(5, 2) == 10
92
+
57
93
 
58
94
  def test_function_pipelining(isolated_client):
59
95
  @isolated_client("virtualenv")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes