prefect-client 3.1.13__py3-none-any.whl → 3.1.15__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.
prefect/flows.py CHANGED
@@ -82,6 +82,7 @@ from prefect.types import BANNED_CHARACTERS, WITHOUT_BANNED_CHARACTERS
82
82
  from prefect.types.entrypoint import EntrypointType
83
83
  from prefect.utilities.annotations import NotSet
84
84
  from prefect.utilities.asyncutils import (
85
+ run_coro_as_sync,
85
86
  run_sync_in_worker_thread,
86
87
  sync_compatible,
87
88
  )
@@ -97,7 +98,7 @@ from prefect.utilities.filesystem import relative_path_to_current_platform
97
98
  from prefect.utilities.hashing import file_hash
98
99
  from prefect.utilities.importtools import import_object, safe_load_namespace
99
100
 
100
- from ._internal.compatibility.async_dispatch import is_in_async_context
101
+ from ._internal.compatibility.async_dispatch import async_dispatch, is_in_async_context
101
102
  from ._internal.pydantic.v2_schema import is_v2_type
102
103
  from ._internal.pydantic.v2_validated_func import V2ValidatedFunction
103
104
  from ._internal.pydantic.v2_validated_func import (
@@ -127,9 +128,9 @@ if TYPE_CHECKING:
127
128
  import logging
128
129
 
129
130
  from prefect.client.orchestration import PrefectClient
131
+ from prefect.client.schemas.objects import FlowRun
130
132
  from prefect.client.types.flexible_schedule_list import FlexibleScheduleList
131
133
  from prefect.deployments.runner import RunnerDeployment
132
- from prefect.flows import FlowRun
133
134
  from prefect.runner.storage import RunnerStorage
134
135
 
135
136
  logger: "logging.Logger" = get_logger("flows")
@@ -654,8 +655,7 @@ class Flow(Generic[P, R]):
654
655
  serialized_parameters[key] = f"<{type(value).__name__}>"
655
656
  return serialized_parameters
656
657
 
657
- @sync_compatible
658
- async def to_deployment(
658
+ async def ato_deployment(
659
659
  self,
660
660
  name: str,
661
661
  interval: Optional[
@@ -684,7 +684,7 @@ class Flow(Generic[P, R]):
684
684
  _sla: Optional[Union[SlaTypes, list[SlaTypes]]] = None, # experimental
685
685
  ) -> "RunnerDeployment":
686
686
  """
687
- Creates a runner deployment object for this flow.
687
+ Asynchronously creates a runner deployment object for this flow.
688
688
 
689
689
  Args:
690
690
  name: The name to give the created deployment.
@@ -740,7 +740,7 @@ class Flow(Generic[P, R]):
740
740
  _raise_on_name_with_banned_characters(name)
741
741
 
742
742
  if self._storage and self._entrypoint:
743
- return await RunnerDeployment.from_storage(
743
+ return await RunnerDeployment.afrom_storage(
744
744
  storage=self._storage,
745
745
  entrypoint=self._entrypoint,
746
746
  name=name,
@@ -761,7 +761,142 @@ class Flow(Generic[P, R]):
761
761
  work_queue_name=work_queue_name,
762
762
  job_variables=job_variables,
763
763
  _sla=_sla,
764
- ) # type: ignore # TODO: remove sync_compatible
764
+ )
765
+ else:
766
+ return RunnerDeployment.from_flow(
767
+ flow=self,
768
+ name=name,
769
+ interval=interval,
770
+ cron=cron,
771
+ rrule=rrule,
772
+ paused=paused,
773
+ schedules=schedules,
774
+ concurrency_limit=concurrency_limit,
775
+ tags=tags,
776
+ triggers=triggers,
777
+ parameters=parameters or {},
778
+ description=description,
779
+ version=version,
780
+ enforce_parameter_schema=enforce_parameter_schema,
781
+ work_pool_name=work_pool_name,
782
+ work_queue_name=work_queue_name,
783
+ job_variables=job_variables,
784
+ entrypoint_type=entrypoint_type,
785
+ _sla=_sla,
786
+ )
787
+
788
+ @async_dispatch(ato_deployment)
789
+ def to_deployment(
790
+ self,
791
+ name: str,
792
+ interval: Optional[
793
+ Union[
794
+ Iterable[Union[int, float, datetime.timedelta]],
795
+ int,
796
+ float,
797
+ datetime.timedelta,
798
+ ]
799
+ ] = None,
800
+ cron: Optional[Union[Iterable[str], str]] = None,
801
+ rrule: Optional[Union[Iterable[str], str]] = None,
802
+ paused: Optional[bool] = None,
803
+ schedules: Optional["FlexibleScheduleList"] = None,
804
+ concurrency_limit: Optional[Union[int, ConcurrencyLimitConfig, None]] = None,
805
+ parameters: Optional[dict[str, Any]] = None,
806
+ triggers: Optional[list[Union[DeploymentTriggerTypes, TriggerTypes]]] = None,
807
+ description: Optional[str] = None,
808
+ tags: Optional[list[str]] = None,
809
+ version: Optional[str] = None,
810
+ enforce_parameter_schema: bool = True,
811
+ work_pool_name: Optional[str] = None,
812
+ work_queue_name: Optional[str] = None,
813
+ job_variables: Optional[dict[str, Any]] = None,
814
+ entrypoint_type: EntrypointType = EntrypointType.FILE_PATH,
815
+ _sla: Optional[Union[SlaTypes, list[SlaTypes]]] = None, # experimental
816
+ ) -> "RunnerDeployment":
817
+ """
818
+ Creates a runner deployment object for this flow.
819
+
820
+ Args:
821
+ name: The name to give the created deployment.
822
+ interval: An interval on which to execute the new deployment. Accepts either a number
823
+ or a timedelta object. If a number is given, it will be interpreted as seconds.
824
+ cron: A cron schedule of when to execute runs of this deployment.
825
+ rrule: An rrule schedule of when to execute runs of this deployment.
826
+ paused: Whether or not to set this deployment as paused.
827
+ schedules: A list of schedule objects defining when to execute runs of this deployment.
828
+ Used to define multiple schedules or additional scheduling options such as `timezone`.
829
+ concurrency_limit: The maximum number of runs of this deployment that can run at the same time.
830
+ parameters: A dictionary of default parameter values to pass to runs of this deployment.
831
+ triggers: A list of triggers that will kick off runs of this deployment.
832
+ description: A description for the created deployment. Defaults to the flow's
833
+ description if not provided.
834
+ tags: A list of tags to associate with the created deployment for organizational
835
+ purposes.
836
+ version: A version for the created deployment. Defaults to the flow's version.
837
+ enforce_parameter_schema: Whether or not the Prefect API should enforce the
838
+ parameter schema for the created deployment.
839
+ work_pool_name: The name of the work pool to use for this deployment.
840
+ work_queue_name: The name of the work queue to use for this deployment's scheduled runs.
841
+ If not provided the default work queue for the work pool will be used.
842
+ job_variables: Settings used to override the values specified default base job template
843
+ of the chosen work pool. Refer to the base job template of the chosen work pool for
844
+ entrypoint_type: Type of entrypoint to use for the deployment. When using a module path
845
+ entrypoint, ensure that the module will be importable in the execution environment.
846
+ _sla: (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
847
+
848
+ Examples:
849
+ Prepare two deployments and serve them:
850
+
851
+ ```python
852
+ from prefect import flow, serve
853
+
854
+ @flow
855
+ def my_flow(name):
856
+ print(f"hello {name}")
857
+
858
+ @flow
859
+ def my_other_flow(name):
860
+ print(f"goodbye {name}")
861
+
862
+ if __name__ == "__main__":
863
+ hello_deploy = my_flow.to_deployment("hello", tags=["dev"])
864
+ bye_deploy = my_other_flow.to_deployment("goodbye", tags=["dev"])
865
+ serve(hello_deploy, bye_deploy)
866
+ ```
867
+ """
868
+ from prefect.deployments.runner import RunnerDeployment
869
+
870
+ if not name.endswith(".py"):
871
+ _raise_on_name_with_banned_characters(name)
872
+
873
+ if self._storage and self._entrypoint:
874
+ return cast(
875
+ RunnerDeployment,
876
+ RunnerDeployment.from_storage(
877
+ storage=self._storage,
878
+ entrypoint=self._entrypoint,
879
+ name=name,
880
+ flow_name=self.name,
881
+ interval=interval,
882
+ cron=cron,
883
+ rrule=rrule,
884
+ paused=paused,
885
+ schedules=schedules,
886
+ concurrency_limit=concurrency_limit,
887
+ tags=tags,
888
+ triggers=triggers,
889
+ parameters=parameters or {},
890
+ description=description,
891
+ version=version,
892
+ enforce_parameter_schema=enforce_parameter_schema,
893
+ work_pool_name=work_pool_name,
894
+ work_queue_name=work_queue_name,
895
+ job_variables=job_variables,
896
+ _sla=_sla,
897
+ _sync=True, # pyright: ignore[reportCallIssue] _sync is valid because .from_storage is decorated with async_dispatch
898
+ ),
899
+ )
765
900
  else:
766
901
  return RunnerDeployment.from_flow(
767
902
  flow=self,
@@ -957,14 +1092,13 @@ class Flow(Generic[P, R]):
957
1092
  loop.stop()
958
1093
 
959
1094
  @classmethod
960
- @sync_compatible
961
- async def from_source(
1095
+ async def afrom_source(
962
1096
  cls,
963
1097
  source: Union[str, "RunnerStorage", ReadableDeploymentStorage],
964
1098
  entrypoint: str,
965
1099
  ) -> "Flow[..., Any]":
966
1100
  """
967
- Loads a flow from a remote source.
1101
+ Loads a flow from a remote source asynchronously.
968
1102
 
969
1103
  Args:
970
1104
  source: Either a URL to a git repository or a storage object.
@@ -1070,6 +1204,115 @@ class Flow(Generic[P, R]):
1070
1204
 
1071
1205
  return flow
1072
1206
 
1207
+ @classmethod
1208
+ @async_dispatch(afrom_source)
1209
+ def from_source(
1210
+ cls,
1211
+ source: Union[str, "RunnerStorage", ReadableDeploymentStorage],
1212
+ entrypoint: str,
1213
+ ) -> "Flow[..., Any]":
1214
+ """
1215
+ Loads a flow from a remote source.
1216
+
1217
+ Args:
1218
+ source: Either a URL to a git repository or a storage object.
1219
+ entrypoint: The path to a file containing a flow and the name of the flow function in
1220
+ the format `./path/to/file.py:flow_func_name`.
1221
+
1222
+ Returns:
1223
+ A new `Flow` instance.
1224
+
1225
+ Examples:
1226
+ Load a flow from a public git repository:
1227
+
1228
+
1229
+ ```python
1230
+ from prefect import flow
1231
+ from prefect.runner.storage import GitRepository
1232
+ from prefect.blocks.system import Secret
1233
+
1234
+ my_flow = flow.from_source(
1235
+ source="https://github.com/org/repo.git",
1236
+ entrypoint="flows.py:my_flow",
1237
+ )
1238
+
1239
+ my_flow()
1240
+ ```
1241
+
1242
+ Load a flow from a private git repository using an access token stored in a `Secret` block:
1243
+
1244
+ ```python
1245
+ from prefect import flow
1246
+ from prefect.runner.storage import GitRepository
1247
+ from prefect.blocks.system import Secret
1248
+
1249
+ my_flow = flow.from_source(
1250
+ source=GitRepository(
1251
+ url="https://github.com/org/repo.git",
1252
+ credentials={"access_token": Secret.load("github-access-token")}
1253
+ ),
1254
+ entrypoint="flows.py:my_flow",
1255
+ )
1256
+
1257
+ my_flow()
1258
+ ```
1259
+
1260
+ Load a flow from a local directory:
1261
+
1262
+ ``` python
1263
+ # from_local_source.py
1264
+
1265
+ from pathlib import Path
1266
+ from prefect import flow
1267
+
1268
+ @flow(log_prints=True)
1269
+ def my_flow(name: str = "world"):
1270
+ print(f"Hello {name}! I'm a flow from a Python script!")
1271
+
1272
+ if __name__ == "__main__":
1273
+ my_flow.from_source(
1274
+ source=str(Path(__file__).parent),
1275
+ entrypoint="from_local_source.py:my_flow",
1276
+ ).deploy(
1277
+ name="my-deployment",
1278
+ parameters=dict(name="Marvin"),
1279
+ work_pool_name="local",
1280
+ )
1281
+ ```
1282
+ """
1283
+
1284
+ from prefect.runner.storage import (
1285
+ BlockStorageAdapter,
1286
+ LocalStorage,
1287
+ RunnerStorage,
1288
+ create_storage_from_source,
1289
+ )
1290
+
1291
+ if isinstance(source, (Path, str)):
1292
+ if isinstance(source, Path):
1293
+ source = str(source)
1294
+ storage = create_storage_from_source(source)
1295
+ elif isinstance(source, RunnerStorage):
1296
+ storage = source
1297
+ elif hasattr(source, "get_directory"):
1298
+ storage = BlockStorageAdapter(source)
1299
+ else:
1300
+ raise TypeError(
1301
+ f"Unsupported source type {type(source).__name__!r}. Please provide a"
1302
+ " URL to remote storage or a storage object."
1303
+ )
1304
+ with tempfile.TemporaryDirectory() as tmpdir:
1305
+ if not isinstance(storage, LocalStorage):
1306
+ storage.set_base_path(Path(tmpdir))
1307
+ run_coro_as_sync(storage.pull_code())
1308
+
1309
+ full_entrypoint = str(storage.destination / entrypoint)
1310
+ flow = load_flow_from_entrypoint(full_entrypoint)
1311
+ flow._storage = storage
1312
+ flow._entrypoint = entrypoint
1313
+
1314
+ return flow
1315
+
1073
1316
  @sync_compatible
1074
1317
  async def deploy(
1075
1318
  self,
@@ -94,12 +94,9 @@ def setup_logging(incremental: Optional[bool] = None) -> dict[str, Any]:
94
94
 
95
95
  for logger_name in PREFECT_LOGGING_EXTRA_LOGGERS.value():
96
96
  logger = logging.getLogger(logger_name)
97
- for handler in extra_config.handlers:
98
- if not config["incremental"]:
97
+ if not config["incremental"]:
98
+ for handler in extra_config.handlers:
99
99
  logger.addHandler(handler)
100
- if logger.level == logging.NOTSET:
101
- logger.setLevel(extra_config.level)
102
- logger.propagate = extra_config.propagate
103
100
 
104
101
  PROCESS_LOGGING_CONFIG = config
105
102
 
@@ -24,7 +24,6 @@ else:
24
24
  LoggingAdapter = logging.LoggerAdapter
25
25
 
26
26
  if TYPE_CHECKING:
27
- from prefect.client.schemas import FlowRun as ClientFlowRun
28
27
  from prefect.client.schemas.objects import FlowRun, TaskRun
29
28
  from prefect.context import RunContext
30
29
  from prefect.flows import Flow
@@ -164,7 +163,7 @@ def get_run_logger(
164
163
 
165
164
 
166
165
  def flow_run_logger(
167
- flow_run: Union["FlowRun", "ClientFlowRun"],
166
+ flow_run: "FlowRun",
168
167
  flow: Optional["Flow[Any, Any]"] = None,
169
168
  **kwargs: str,
170
169
  ) -> PrefectLogAdapter: