prefect-client 2.19.2__py3-none-any.whl → 3.0.0rc1__py3-none-any.whl

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 (239) hide show
  1. prefect/__init__.py +8 -56
  2. prefect/_internal/compatibility/deprecated.py +6 -115
  3. prefect/_internal/compatibility/experimental.py +4 -79
  4. prefect/_internal/concurrency/api.py +0 -34
  5. prefect/_internal/concurrency/calls.py +0 -6
  6. prefect/_internal/concurrency/cancellation.py +0 -3
  7. prefect/_internal/concurrency/event_loop.py +0 -20
  8. prefect/_internal/concurrency/inspection.py +3 -3
  9. prefect/_internal/concurrency/threads.py +35 -0
  10. prefect/_internal/concurrency/waiters.py +0 -28
  11. prefect/_internal/pydantic/__init__.py +0 -45
  12. prefect/_internal/pydantic/v1_schema.py +21 -22
  13. prefect/_internal/pydantic/v2_schema.py +0 -2
  14. prefect/_internal/pydantic/v2_validated_func.py +18 -23
  15. prefect/_internal/schemas/bases.py +44 -177
  16. prefect/_internal/schemas/fields.py +1 -43
  17. prefect/_internal/schemas/validators.py +60 -158
  18. prefect/artifacts.py +161 -14
  19. prefect/automations.py +39 -4
  20. prefect/blocks/abstract.py +1 -1
  21. prefect/blocks/core.py +268 -148
  22. prefect/blocks/fields.py +2 -57
  23. prefect/blocks/kubernetes.py +8 -12
  24. prefect/blocks/notifications.py +40 -20
  25. prefect/blocks/system.py +22 -11
  26. prefect/blocks/webhook.py +2 -9
  27. prefect/client/base.py +4 -4
  28. prefect/client/cloud.py +8 -13
  29. prefect/client/orchestration.py +347 -341
  30. prefect/client/schemas/actions.py +92 -86
  31. prefect/client/schemas/filters.py +20 -40
  32. prefect/client/schemas/objects.py +151 -145
  33. prefect/client/schemas/responses.py +16 -24
  34. prefect/client/schemas/schedules.py +47 -35
  35. prefect/client/subscriptions.py +2 -2
  36. prefect/client/utilities.py +5 -2
  37. prefect/concurrency/asyncio.py +3 -1
  38. prefect/concurrency/events.py +1 -1
  39. prefect/concurrency/services.py +6 -3
  40. prefect/context.py +195 -27
  41. prefect/deployments/__init__.py +5 -6
  42. prefect/deployments/base.py +7 -5
  43. prefect/deployments/flow_runs.py +185 -0
  44. prefect/deployments/runner.py +50 -45
  45. prefect/deployments/schedules.py +28 -23
  46. prefect/deployments/steps/__init__.py +0 -1
  47. prefect/deployments/steps/core.py +1 -0
  48. prefect/deployments/steps/pull.py +7 -21
  49. prefect/engine.py +12 -2422
  50. prefect/events/actions.py +17 -23
  51. prefect/events/cli/automations.py +19 -6
  52. prefect/events/clients.py +14 -37
  53. prefect/events/filters.py +14 -18
  54. prefect/events/related.py +2 -2
  55. prefect/events/schemas/__init__.py +0 -5
  56. prefect/events/schemas/automations.py +55 -46
  57. prefect/events/schemas/deployment_triggers.py +7 -197
  58. prefect/events/schemas/events.py +34 -65
  59. prefect/events/schemas/labelling.py +10 -14
  60. prefect/events/utilities.py +2 -3
  61. prefect/events/worker.py +2 -3
  62. prefect/filesystems.py +6 -517
  63. prefect/{new_flow_engine.py → flow_engine.py} +313 -72
  64. prefect/flow_runs.py +377 -5
  65. prefect/flows.py +307 -166
  66. prefect/futures.py +186 -345
  67. prefect/infrastructure/__init__.py +0 -27
  68. prefect/infrastructure/provisioners/__init__.py +5 -3
  69. prefect/infrastructure/provisioners/cloud_run.py +11 -6
  70. prefect/infrastructure/provisioners/container_instance.py +11 -7
  71. prefect/infrastructure/provisioners/ecs.py +6 -4
  72. prefect/infrastructure/provisioners/modal.py +8 -5
  73. prefect/input/actions.py +2 -4
  74. prefect/input/run_input.py +5 -7
  75. prefect/logging/formatters.py +0 -2
  76. prefect/logging/handlers.py +3 -11
  77. prefect/logging/loggers.py +2 -2
  78. prefect/manifests.py +2 -1
  79. prefect/records/__init__.py +1 -0
  80. prefect/records/result_store.py +42 -0
  81. prefect/records/store.py +9 -0
  82. prefect/results.py +43 -39
  83. prefect/runner/runner.py +19 -15
  84. prefect/runner/server.py +6 -10
  85. prefect/runner/storage.py +3 -8
  86. prefect/runner/submit.py +2 -2
  87. prefect/runner/utils.py +2 -2
  88. prefect/serializers.py +24 -35
  89. prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
  90. prefect/settings.py +70 -133
  91. prefect/states.py +17 -47
  92. prefect/task_engine.py +697 -58
  93. prefect/task_runners.py +269 -301
  94. prefect/task_server.py +53 -34
  95. prefect/tasks.py +327 -337
  96. prefect/transactions.py +220 -0
  97. prefect/types/__init__.py +61 -82
  98. prefect/utilities/asyncutils.py +195 -136
  99. prefect/utilities/callables.py +311 -43
  100. prefect/utilities/collections.py +23 -38
  101. prefect/utilities/dispatch.py +11 -3
  102. prefect/utilities/dockerutils.py +4 -0
  103. prefect/utilities/engine.py +140 -20
  104. prefect/utilities/importtools.py +97 -27
  105. prefect/utilities/pydantic.py +128 -38
  106. prefect/utilities/schema_tools/hydration.py +5 -1
  107. prefect/utilities/templating.py +12 -2
  108. prefect/variables.py +78 -61
  109. prefect/workers/__init__.py +0 -1
  110. prefect/workers/base.py +15 -17
  111. prefect/workers/process.py +3 -8
  112. prefect/workers/server.py +2 -2
  113. {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/METADATA +22 -21
  114. prefect_client-3.0.0rc1.dist-info/RECORD +176 -0
  115. prefect/_internal/pydantic/_base_model.py +0 -51
  116. prefect/_internal/pydantic/_compat.py +0 -82
  117. prefect/_internal/pydantic/_flags.py +0 -20
  118. prefect/_internal/pydantic/_types.py +0 -8
  119. prefect/_internal/pydantic/utilities/__init__.py +0 -0
  120. prefect/_internal/pydantic/utilities/config_dict.py +0 -72
  121. prefect/_internal/pydantic/utilities/field_validator.py +0 -150
  122. prefect/_internal/pydantic/utilities/model_construct.py +0 -56
  123. prefect/_internal/pydantic/utilities/model_copy.py +0 -55
  124. prefect/_internal/pydantic/utilities/model_dump.py +0 -136
  125. prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
  126. prefect/_internal/pydantic/utilities/model_fields.py +0 -50
  127. prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
  128. prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
  129. prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
  130. prefect/_internal/pydantic/utilities/model_validate.py +0 -75
  131. prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
  132. prefect/_internal/pydantic/utilities/model_validator.py +0 -87
  133. prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
  134. prefect/_vendor/__init__.py +0 -0
  135. prefect/_vendor/fastapi/__init__.py +0 -25
  136. prefect/_vendor/fastapi/applications.py +0 -946
  137. prefect/_vendor/fastapi/background.py +0 -3
  138. prefect/_vendor/fastapi/concurrency.py +0 -44
  139. prefect/_vendor/fastapi/datastructures.py +0 -58
  140. prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
  141. prefect/_vendor/fastapi/dependencies/models.py +0 -64
  142. prefect/_vendor/fastapi/dependencies/utils.py +0 -877
  143. prefect/_vendor/fastapi/encoders.py +0 -177
  144. prefect/_vendor/fastapi/exception_handlers.py +0 -40
  145. prefect/_vendor/fastapi/exceptions.py +0 -46
  146. prefect/_vendor/fastapi/logger.py +0 -3
  147. prefect/_vendor/fastapi/middleware/__init__.py +0 -1
  148. prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
  149. prefect/_vendor/fastapi/middleware/cors.py +0 -3
  150. prefect/_vendor/fastapi/middleware/gzip.py +0 -3
  151. prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
  152. prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
  153. prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
  154. prefect/_vendor/fastapi/openapi/__init__.py +0 -0
  155. prefect/_vendor/fastapi/openapi/constants.py +0 -2
  156. prefect/_vendor/fastapi/openapi/docs.py +0 -203
  157. prefect/_vendor/fastapi/openapi/models.py +0 -480
  158. prefect/_vendor/fastapi/openapi/utils.py +0 -485
  159. prefect/_vendor/fastapi/param_functions.py +0 -340
  160. prefect/_vendor/fastapi/params.py +0 -453
  161. prefect/_vendor/fastapi/requests.py +0 -4
  162. prefect/_vendor/fastapi/responses.py +0 -40
  163. prefect/_vendor/fastapi/routing.py +0 -1331
  164. prefect/_vendor/fastapi/security/__init__.py +0 -15
  165. prefect/_vendor/fastapi/security/api_key.py +0 -98
  166. prefect/_vendor/fastapi/security/base.py +0 -6
  167. prefect/_vendor/fastapi/security/http.py +0 -172
  168. prefect/_vendor/fastapi/security/oauth2.py +0 -227
  169. prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
  170. prefect/_vendor/fastapi/security/utils.py +0 -10
  171. prefect/_vendor/fastapi/staticfiles.py +0 -1
  172. prefect/_vendor/fastapi/templating.py +0 -3
  173. prefect/_vendor/fastapi/testclient.py +0 -1
  174. prefect/_vendor/fastapi/types.py +0 -3
  175. prefect/_vendor/fastapi/utils.py +0 -235
  176. prefect/_vendor/fastapi/websockets.py +0 -7
  177. prefect/_vendor/starlette/__init__.py +0 -1
  178. prefect/_vendor/starlette/_compat.py +0 -28
  179. prefect/_vendor/starlette/_exception_handler.py +0 -80
  180. prefect/_vendor/starlette/_utils.py +0 -88
  181. prefect/_vendor/starlette/applications.py +0 -261
  182. prefect/_vendor/starlette/authentication.py +0 -159
  183. prefect/_vendor/starlette/background.py +0 -43
  184. prefect/_vendor/starlette/concurrency.py +0 -59
  185. prefect/_vendor/starlette/config.py +0 -151
  186. prefect/_vendor/starlette/convertors.py +0 -87
  187. prefect/_vendor/starlette/datastructures.py +0 -707
  188. prefect/_vendor/starlette/endpoints.py +0 -130
  189. prefect/_vendor/starlette/exceptions.py +0 -60
  190. prefect/_vendor/starlette/formparsers.py +0 -276
  191. prefect/_vendor/starlette/middleware/__init__.py +0 -17
  192. prefect/_vendor/starlette/middleware/authentication.py +0 -52
  193. prefect/_vendor/starlette/middleware/base.py +0 -220
  194. prefect/_vendor/starlette/middleware/cors.py +0 -176
  195. prefect/_vendor/starlette/middleware/errors.py +0 -265
  196. prefect/_vendor/starlette/middleware/exceptions.py +0 -74
  197. prefect/_vendor/starlette/middleware/gzip.py +0 -113
  198. prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
  199. prefect/_vendor/starlette/middleware/sessions.py +0 -82
  200. prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
  201. prefect/_vendor/starlette/middleware/wsgi.py +0 -147
  202. prefect/_vendor/starlette/requests.py +0 -328
  203. prefect/_vendor/starlette/responses.py +0 -347
  204. prefect/_vendor/starlette/routing.py +0 -933
  205. prefect/_vendor/starlette/schemas.py +0 -154
  206. prefect/_vendor/starlette/staticfiles.py +0 -248
  207. prefect/_vendor/starlette/status.py +0 -199
  208. prefect/_vendor/starlette/templating.py +0 -231
  209. prefect/_vendor/starlette/testclient.py +0 -804
  210. prefect/_vendor/starlette/types.py +0 -30
  211. prefect/_vendor/starlette/websockets.py +0 -193
  212. prefect/agent.py +0 -698
  213. prefect/deployments/deployments.py +0 -1042
  214. prefect/deprecated/__init__.py +0 -0
  215. prefect/deprecated/data_documents.py +0 -350
  216. prefect/deprecated/packaging/__init__.py +0 -12
  217. prefect/deprecated/packaging/base.py +0 -96
  218. prefect/deprecated/packaging/docker.py +0 -146
  219. prefect/deprecated/packaging/file.py +0 -92
  220. prefect/deprecated/packaging/orion.py +0 -80
  221. prefect/deprecated/packaging/serializers.py +0 -171
  222. prefect/events/instrument.py +0 -135
  223. prefect/infrastructure/base.py +0 -323
  224. prefect/infrastructure/container.py +0 -818
  225. prefect/infrastructure/kubernetes.py +0 -920
  226. prefect/infrastructure/process.py +0 -289
  227. prefect/new_task_engine.py +0 -423
  228. prefect/pydantic/__init__.py +0 -76
  229. prefect/pydantic/main.py +0 -39
  230. prefect/software/__init__.py +0 -2
  231. prefect/software/base.py +0 -50
  232. prefect/software/conda.py +0 -199
  233. prefect/software/pip.py +0 -122
  234. prefect/software/python.py +0 -52
  235. prefect/workers/block.py +0 -218
  236. prefect_client-2.19.2.dist-info/RECORD +0 -292
  237. {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/LICENSE +0 -0
  238. {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/WHEEL +0 -0
  239. {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/top_level.txt +0 -0
prefect/software/conda.py DELETED
@@ -1,199 +0,0 @@
1
- import json
2
- import re
3
- import subprocess
4
- import sys
5
- from pathlib import Path
6
- from typing import List, Type
7
-
8
- import yaml
9
-
10
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
11
-
12
- if HAS_PYDANTIC_V2:
13
- from pydantic.v1 import Field, validate_arguments
14
- else:
15
- from pydantic import Field, validate_arguments
16
- from typing_extensions import Self
17
-
18
- from prefect.software.base import (
19
- Requirement,
20
- pop_requirement_by_name,
21
- remove_duplicate_requirements,
22
- )
23
- from prefect.software.pip import current_environment_requirements
24
- from prefect.software.python import PythonEnvironment
25
- from prefect.utilities.collections import listrepr
26
-
27
- # Capture each component of a conda requirement string.
28
- #
29
- # <name><version_specifier><version><build_specifier><build>
30
- #
31
- # The specification of these requirements is more relaxed than pip requirements.
32
- # Notably, the version specifier is typically a single equal sign and a build can be
33
- # included after the version.
34
- #
35
- # For example, the requirement "requests=2.25.1=pyhd3eb1b0_0" matches as:
36
- # name: "requests"
37
- # version_specifier: "="
38
- # version: "2.25.1"
39
- # build_specifier: "="
40
- # build: "pyhd3eb1b0_0"
41
- #
42
- # Regex components:
43
- # name (required): any combination of numbers, letters, or dashes
44
- # version_specifier (optional): one or more of `>`, `<`, and `=`
45
- # version (optional): any combination of numbers, letters, or periods; not valid
46
- # unless grouped with a version specifier.
47
- # build_specifier (optional): just `=`.
48
- # build (optional): any combination of numbers, letters, or underscores; not valid
49
- # unless grouped with a build specifier.
50
-
51
- CONDA_REQUIREMENT = re.compile(
52
- r"^((?P<channel>[0-9A-Za-z\_\-\/]+)::)?"
53
- r"(?P<name>[0-9A-Za-z_\-\.]+)"
54
- r"((?P<version_specifier>[>=<]+)(?P<version>[0-9a-zA-Z\.]+))?"
55
- r"((?P<build_specifier>=)(?P<build>[0-9A-Za-z\_\[\]\=]+))?$"
56
- )
57
-
58
-
59
- class CondaRequirement(Requirement):
60
- """
61
- A parsed requirement for installation with conda.
62
- """
63
-
64
- def __init__(self, requirement_string: str):
65
- self._requirement_string = requirement_string
66
-
67
- parsed = CONDA_REQUIREMENT.match(requirement_string)
68
- if parsed is None:
69
- raise ValueError(
70
- f"Invalid requirement {requirement_string!r}: could not be parsed."
71
- )
72
- self._parts = parsed.groupdict()
73
-
74
- self.name = self._parts["name"]
75
- self.version_specifier = self._parts["version_specifier"]
76
- self.version = self._parts["version"]
77
- self.build_specifier = self._parts["build_specifier"]
78
- self.build = self._parts["build"]
79
-
80
- def __str__(self) -> str:
81
- return self._requirement_string
82
-
83
-
84
- class CondaError(RuntimeError):
85
- """
86
- Raised if an error occurs in conda.
87
- """
88
-
89
-
90
- def current_environment_conda_requirements(
91
- include_builds: bool = False, explicit_only: bool = True
92
- ) -> List[CondaRequirement]:
93
- """
94
- Return conda requirements by exporting the current environment.
95
-
96
- Skips any pip requirements included in the export. Only requirements that are
97
- managed by conda are returned.
98
- """
99
- command = ["conda", "env", "export", "--json"]
100
-
101
- if not include_builds:
102
- command.append("--no-builds")
103
- if explicit_only:
104
- command.append("--from-history")
105
-
106
- process = subprocess.run(command, capture_output=True)
107
- parsed = json.loads(process.stdout)
108
- if "error" in parsed:
109
- raise CondaError(
110
- "Encountered an exception while exporting the conda environment: "
111
- + parsed["error"]
112
- )
113
-
114
- # If no dependencies are given, this field will not be present
115
- dependencies = parsed.get("dependencies", [])
116
-
117
- # The string check will exclude nested objects like the 'pip' subtree
118
- return [CondaRequirement(dep) for dep in dependencies if isinstance(dep, str)]
119
-
120
-
121
- class CondaEnvironment(PythonEnvironment):
122
- conda_requirements: List[CondaRequirement] = Field(default_factory=list)
123
-
124
- @classmethod
125
- def from_environment(cls: Type[Self], exclude_nested: bool = False) -> Self:
126
- conda_requirements = (
127
- current_environment_conda_requirements()
128
- if "conda" in sys.executable
129
- else []
130
- )
131
- pip_requirements = remove_duplicate_requirements(
132
- conda_requirements,
133
- current_environment_requirements(
134
- exclude_nested=exclude_nested, on_uninstallable_requirement="warn"
135
- ),
136
- )
137
- python_requirement = pop_requirement_by_name(conda_requirements, "python")
138
- python_version = python_requirement.version if python_requirement else None
139
-
140
- return cls(
141
- pip_requirements=pip_requirements,
142
- conda_requirements=conda_requirements,
143
- python_version=python_version,
144
- )
145
-
146
- @classmethod
147
- @validate_arguments
148
- def from_file(cls: Type[Self], path: Path) -> Self:
149
- parsed = yaml.safe_load(path.read_text())
150
-
151
- # If no dependencies are given, this field will not be present
152
- dependencies = parsed.get("dependencies", [])
153
-
154
- # The string check will exclude nested objects like the 'pip' subtree
155
- conda_requirements = [
156
- CondaRequirement(dep) for dep in dependencies if isinstance(dep, str)
157
- ]
158
-
159
- python_requirement = pop_requirement_by_name(conda_requirements, "python")
160
- python_version = python_requirement.version if python_requirement else None
161
-
162
- other_requirements = {}
163
-
164
- # Parse nested requirements. We only support 'pip' for now but we'll check for
165
- # others
166
- for subtree in [dep for dep in dependencies if isinstance(dep, dict)]:
167
- key = list(subtree.keys())[0]
168
-
169
- if key in other_requirements:
170
- raise ValueError(
171
- "Invalid conda requirements specification. "
172
- f"Found duplicate key {key!r}."
173
- )
174
-
175
- other_requirements[key] = subtree[key]
176
-
177
- pip_requirements = other_requirements.pop("pip", [])
178
-
179
- if other_requirements:
180
- raise ValueError(
181
- "Found unsupported requirements types in file: "
182
- f"{listrepr(other_requirements.keys(), ', ')}"
183
- )
184
-
185
- return cls(
186
- conda_requirements=conda_requirements,
187
- pip_requirements=pip_requirements,
188
- python_version=python_version,
189
- )
190
-
191
- def install_commands(self) -> List[str]:
192
- pip_install_commands = super().install_commands()
193
-
194
- if not self.conda_requirements:
195
- return pip_install_commands
196
-
197
- return [
198
- ["conda", "install", *(str(req) for req in self.conda_requirements)]
199
- ] + pip_install_commands
prefect/software/pip.py DELETED
@@ -1,122 +0,0 @@
1
- import site
2
- import warnings
3
- from pathlib import Path
4
- from typing import Dict, List, Type, Union
5
-
6
- import packaging.requirements
7
- import packaging.version
8
- from typing_extensions import Literal, Self
9
-
10
- from prefect.software.base import Requirement
11
- from prefect.utilities.compat import importlib_metadata
12
-
13
-
14
- class PipRequirement(Requirement, packaging.requirements.Requirement):
15
- """
16
- A parsed Python requirement.
17
-
18
- An extension for `packaging.version.Requirement` with Pydantic support.
19
- """
20
-
21
- @classmethod
22
- def from_distribution(
23
- cls: Type[Self], dist: "importlib_metadata.Distribution"
24
- ) -> Self:
25
- """
26
- Convert a Python distribution object into a requirement
27
- """
28
- if _is_editable_install(dist):
29
- raise ValueError(
30
- f"Distribution {dist!r} is an editable installation and cannot be "
31
- "used as a requirement."
32
- )
33
-
34
- return cls.validate(
35
- packaging.requirements.Requirement(
36
- f"{dist.metadata['name']}=={dist.version}"
37
- )
38
- )
39
-
40
-
41
- def _get_installed_distributions() -> Dict[str, "importlib_metadata.Distribution"]:
42
- return {dist.name: dist for dist in importlib_metadata.distributions()}
43
-
44
-
45
- def _is_child_path(path: Union[Path, str], parent: Union[Path, str]) -> bool:
46
- return Path(parent).resolve() in Path(path).resolve().parents
47
-
48
-
49
- def _is_same_path(path: Union[Path, str], other: Union[Path, str]) -> bool:
50
- return Path(path).resolve() == Path(other).resolve()
51
-
52
-
53
- def _is_editable_install(dist: "importlib_metadata.Distribution") -> bool:
54
- """
55
- Determine if a distribution is an 'editable' install by scanning if it is present
56
- in a site-packages directory or not; if not, we presume it is editable.
57
- """
58
- site_packages = site.getsitepackages() + [site.getusersitepackages()]
59
-
60
- dist_location = dist.locate_file("")
61
- for site_package_dir in site_packages:
62
- if _is_same_path(dist_location, site_package_dir) or _is_child_path(
63
- dist_location, site_package_dir
64
- ):
65
- return False
66
-
67
- return True
68
-
69
-
70
- def _remove_distributions_required_by_others(
71
- dists: Dict[str, "importlib_metadata.Distribution"],
72
- ) -> Dict[str, "importlib_metadata.Distribution"]:
73
- # Collect all child requirements
74
- child_requirement_names = set()
75
- for dist in dists.values():
76
- child_requirement_names.update(dist.requires or [])
77
-
78
- return {
79
- name: dist
80
- for name, dist in dists.items()
81
- if name not in child_requirement_names
82
- }
83
-
84
-
85
- def current_environment_requirements(
86
- exclude_nested: bool = False,
87
- on_uninstallable_requirement: Literal["ignore", "warn", "raise"] = "warn",
88
- ) -> List[PipRequirement]:
89
- dists = _get_installed_distributions()
90
- if exclude_nested:
91
- dists = _remove_distributions_required_by_others(dists)
92
-
93
- requirements = []
94
- uninstallable_msgs = []
95
- for dist in dists.values():
96
- if _is_editable_install(dist):
97
- uninstallable_msgs.append(
98
- f"- {dist.name}: This distribution is an editable installation."
99
- )
100
- else:
101
- requirements.append(PipRequirement.from_distribution(dist))
102
-
103
- if uninstallable_msgs:
104
- message = (
105
- "The following requirements will not be installable on another machine:\n"
106
- + "\n".join(uninstallable_msgs)
107
- )
108
- if on_uninstallable_requirement == "ignore":
109
- pass
110
- elif on_uninstallable_requirement == "warn":
111
- # When warning, include a note that these distributions are excluded
112
- warnings.warn(message + "\nThese requirements will be ignored.")
113
- elif on_uninstallable_requirement == "raise":
114
- raise ValueError(message)
115
- else:
116
- raise ValueError(
117
- "Unknown mode for `on_uninstallable_requirement`: "
118
- f"{on_uninstallable_requirement!r}. "
119
- "Expected one of: 'ignore', 'warn', 'raise'."
120
- )
121
-
122
- return requirements
@@ -1,52 +0,0 @@
1
- from pathlib import Path
2
- from typing import List, Type
3
-
4
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
5
- from prefect._internal.schemas.validators import infer_python_version
6
-
7
- if HAS_PYDANTIC_V2:
8
- from pydantic.v1 import BaseModel, Field, validate_arguments, validator
9
- else:
10
- from pydantic import BaseModel, Field, validate_arguments, validator
11
-
12
- from typing_extensions import Self
13
-
14
- from prefect.software.pip import PipRequirement, current_environment_requirements
15
-
16
-
17
- class PythonEnvironment(BaseModel):
18
- """
19
- A specification for a Python environment.
20
- """
21
-
22
- python_version: str = None
23
- pip_requirements: List[PipRequirement] = Field(default_factory=list)
24
-
25
- @validator("python_version", pre=True, always=True)
26
- def validate_python_version(cls, value):
27
- return infer_python_version(value)
28
-
29
- @classmethod
30
- def from_environment(cls: Type[Self], exclude_nested: bool = False) -> Self:
31
- """
32
- Generate requirements from the current environment
33
-
34
- Arguments:
35
- exclude_nested: If True, only top-level requirements will be included.
36
- Defaults to including all requirements.
37
- """
38
- pip_requirements = current_environment_requirements(
39
- exclude_nested=exclude_nested, on_uninstallable_requirement="warn"
40
- )
41
- return cls(pip_requirements=pip_requirements)
42
-
43
- @classmethod
44
- @validate_arguments
45
- def from_file(cls: Type[Self], path: Path) -> Self:
46
- return PythonEnvironment(pip_requirements=path.read_text().strip().splitlines())
47
-
48
- def install_commands(self) -> List[List[str]]:
49
- if not self.pip_requirements:
50
- return []
51
-
52
- return [["pip", "install", *(str(req) for req in self.pip_requirements)]]
prefect/workers/block.py DELETED
@@ -1,218 +0,0 @@
1
- import shlex
2
- from typing import TYPE_CHECKING, Any, Dict, List, Optional
3
-
4
- import anyio
5
- import anyio.abc
6
-
7
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
8
- from prefect._internal.schemas.validators import validate_block_is_infrastructure
9
- from prefect.blocks.core import Block
10
- from prefect.client.schemas.objects import BlockDocument
11
- from prefect.utilities.collections import get_from_dict
12
- from prefect.workers.base import BaseWorker, BaseWorkerResult
13
-
14
- if HAS_PYDANTIC_V2:
15
- from pydantic.v1 import BaseModel, Field, PrivateAttr, validator
16
- else:
17
- from pydantic import BaseModel, Field, PrivateAttr, validator
18
-
19
- from prefect.client.orchestration import PrefectClient
20
- from prefect.client.utilities import inject_client
21
- from prefect.events import RelatedResource
22
- from prefect.events.related import object_as_related_resource, tags_as_related_resources
23
- from prefect.utilities.templating import apply_values
24
-
25
- if TYPE_CHECKING:
26
- from prefect.client.schemas.objects import Flow, FlowRun
27
- from prefect.client.schemas.responses import DeploymentResponse
28
-
29
-
30
- class BlockWorkerJobConfiguration(BaseModel):
31
- block: Block = Field(
32
- default=..., description="The infrastructure block to use for job creation."
33
- )
34
-
35
- @validator("block")
36
- def _validate_infrastructure_block(cls, v):
37
- return validate_block_is_infrastructure(v)
38
-
39
- _related_objects: Dict[str, Any] = PrivateAttr(default_factory=dict)
40
-
41
- @property
42
- def is_using_a_runner(self):
43
- return (
44
- self.block.command is not None
45
- and "prefect flow-run execute" in shlex.join(self.block.command)
46
- )
47
-
48
- @staticmethod
49
- def _get_base_config_defaults(variables: dict) -> dict:
50
- """Get default values from base config for all variables that have them."""
51
- defaults = dict()
52
- for variable_name, attrs in variables.items():
53
- if "default" in attrs:
54
- defaults[variable_name] = attrs["default"]
55
-
56
- return defaults
57
-
58
- @classmethod
59
- @inject_client
60
- async def from_template_and_values(
61
- cls, base_job_template: dict, values: dict, client: "PrefectClient" = None
62
- ):
63
- """Creates a valid worker configuration object from the provided base
64
- configuration and overrides.
65
-
66
- Important: this method expects that the base_job_template was already
67
- validated server-side.
68
- """
69
- job_config: Dict[str, Any] = base_job_template["job_configuration"]
70
- variables_schema = base_job_template["variables"]
71
- variables = cls._get_base_config_defaults(
72
- variables_schema.get("properties", {})
73
- )
74
- variables.update(values)
75
-
76
- populated_configuration = apply_values(template=job_config, values=variables)
77
-
78
- block_document_id = get_from_dict(
79
- populated_configuration, "block.$ref.block_document_id"
80
- )
81
- if not block_document_id:
82
- raise ValueError(
83
- "Base job template is invalid for this worker type because it does not"
84
- " contain a block_document_id after variable resolution."
85
- )
86
-
87
- block_document = await client.read_block_document(
88
- block_document_id=block_document_id
89
- )
90
- infrastructure_block = Block._from_block_document(block_document)
91
-
92
- populated_configuration["block"] = infrastructure_block
93
-
94
- return cls(**populated_configuration)
95
-
96
- @classmethod
97
- def json_template(cls) -> dict:
98
- """Returns a dict with job configuration as keys and the corresponding templates as values
99
-
100
- Defaults to using the job configuration parameter name as the template variable name.
101
-
102
- e.g.
103
- {
104
- key1: '{{ key1 }}', # default variable template
105
- key2: '{{ template2 }}', # `template2` specifically provide as template
106
- }
107
- """
108
- configuration = {}
109
- properties = cls.schema()["properties"]
110
- for k, v in properties.items():
111
- if v.get("template"):
112
- template = v["template"]
113
- else:
114
- template = "{{ " + k + " }}"
115
- configuration[k] = template
116
-
117
- return configuration
118
-
119
- def _related_resources(self) -> List[RelatedResource]:
120
- tags = set()
121
- related = []
122
-
123
- for kind, obj in self._related_objects.items():
124
- if obj is None:
125
- continue
126
- if hasattr(obj, "tags"):
127
- tags.update(obj.tags)
128
- related.append(object_as_related_resource(kind=kind, role=kind, object=obj))
129
-
130
- return related + tags_as_related_resources(tags)
131
-
132
- def prepare_for_flow_run(
133
- self,
134
- flow_run: "FlowRun",
135
- deployment: Optional["DeploymentResponse"] = None,
136
- flow: Optional["Flow"] = None,
137
- ):
138
- self.block = self.block.prepare_for_flow_run(
139
- flow_run=flow_run, deployment=deployment, flow=flow
140
- )
141
-
142
-
143
- class BlockWorkerResult(BaseWorkerResult):
144
- """Result of a block worker job"""
145
-
146
-
147
- class BlockWorker(BaseWorker):
148
- type = "block"
149
- job_configuration = BlockWorkerJobConfiguration
150
-
151
- _description = "Execute flow runs using an infrastructure block as the job creator."
152
- _display_name = "Block"
153
-
154
- async def run(
155
- self,
156
- flow_run: "FlowRun",
157
- configuration: BlockWorkerJobConfiguration,
158
- task_status: Optional[anyio.abc.TaskStatus] = None,
159
- ):
160
- block = configuration.block
161
-
162
- # logic for applying infra overrides taken from src/prefect/agent.py
163
- deployment = await self._client.read_deployment(flow_run.deployment_id)
164
- flow = await self._client.read_flow(deployment.flow_id)
165
- infra_document = await self._client.read_block_document(
166
- configuration.block._block_document_id
167
- )
168
-
169
- # this piece of logic applies any overrides that may have been set on the
170
- # deployment; overrides are defined as dot.delimited paths on possibly nested
171
- # attributes of the infrastructure block
172
- doc_dict = infra_document.dict()
173
- infra_dict = doc_dict.get("data", {})
174
- for override, value in (deployment.job_variables or {}).items():
175
- nested_fields = override.split(".")
176
- if nested_fields == ["command"]:
177
- value = shlex.split(value)
178
- data = infra_dict
179
- for field in nested_fields[:-1]:
180
- data = data[field]
181
-
182
- # once we reach the end, set the value
183
- data[nested_fields[-1]] = value
184
-
185
- # reconstruct the infra block
186
- doc_dict["data"] = infra_dict
187
- infra_document = BlockDocument(**doc_dict)
188
- block = Block._from_block_document(infra_document)
189
-
190
- block = block.prepare_for_flow_run(
191
- flow_run=flow_run, deployment=deployment, flow=flow
192
- )
193
-
194
- result = await block.run(
195
- task_status=task_status,
196
- )
197
- return BlockWorkerResult(
198
- identifier=result.identifier, status_code=result.status_code
199
- )
200
-
201
- async def kill_infrastructure(
202
- self,
203
- infrastructure_pid: str,
204
- configuration: BlockWorkerJobConfiguration,
205
- grace_seconds: int = 30,
206
- ):
207
- block = configuration.block
208
- if not hasattr(block, "kill"):
209
- self._logger.error(
210
- f"Flow run infrastructure block {block.type!r} "
211
- "does not support killing created infrastructure. "
212
- "Cancellation cannot be guaranteed."
213
- )
214
- return
215
-
216
- await block.kill(
217
- infrastructure_pid=infrastructure_pid, grace_seconds=grace_seconds
218
- )