prefect-client 2.18.3__py3-none-any.whl → 2.19.1__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 (42) hide show
  1. prefect/__init__.py +1 -15
  2. prefect/_internal/compatibility/experimental.py +11 -2
  3. prefect/_internal/concurrency/cancellation.py +2 -0
  4. prefect/_internal/schemas/validators.py +10 -0
  5. prefect/_vendor/starlette/testclient.py +1 -1
  6. prefect/blocks/notifications.py +6 -6
  7. prefect/client/base.py +244 -1
  8. prefect/client/cloud.py +4 -2
  9. prefect/client/orchestration.py +515 -106
  10. prefect/client/schemas/actions.py +58 -8
  11. prefect/client/schemas/objects.py +15 -1
  12. prefect/client/schemas/responses.py +19 -0
  13. prefect/client/schemas/schedules.py +1 -1
  14. prefect/client/utilities.py +2 -2
  15. prefect/concurrency/asyncio.py +34 -4
  16. prefect/concurrency/sync.py +40 -6
  17. prefect/context.py +2 -2
  18. prefect/engine.py +2 -2
  19. prefect/events/clients.py +2 -2
  20. prefect/flows.py +91 -17
  21. prefect/infrastructure/process.py +0 -17
  22. prefect/logging/formatters.py +1 -4
  23. prefect/new_flow_engine.py +137 -168
  24. prefect/new_task_engine.py +137 -202
  25. prefect/runner/__init__.py +1 -1
  26. prefect/runner/runner.py +2 -107
  27. prefect/settings.py +21 -0
  28. prefect/tasks.py +76 -57
  29. prefect/types/__init__.py +27 -5
  30. prefect/utilities/annotations.py +1 -8
  31. prefect/utilities/asyncutils.py +4 -0
  32. prefect/utilities/engine.py +106 -1
  33. prefect/utilities/schema_tools/__init__.py +6 -1
  34. prefect/utilities/schema_tools/validation.py +25 -8
  35. prefect/utilities/timeout.py +34 -0
  36. prefect/workers/base.py +7 -3
  37. prefect/workers/process.py +0 -17
  38. {prefect_client-2.18.3.dist-info → prefect_client-2.19.1.dist-info}/METADATA +1 -1
  39. {prefect_client-2.18.3.dist-info → prefect_client-2.19.1.dist-info}/RECORD +42 -41
  40. {prefect_client-2.18.3.dist-info → prefect_client-2.19.1.dist-info}/LICENSE +0 -0
  41. {prefect_client-2.18.3.dist-info → prefect_client-2.19.1.dist-info}/WHEEL +0 -0
  42. {prefect_client-2.18.3.dist-info → prefect_client-2.19.1.dist-info}/top_level.txt +0 -0
@@ -19,14 +19,17 @@ from prefect._internal.schemas.serializers import orjson_dumps_extra_compatible
19
19
  from prefect._internal.schemas.validators import (
20
20
  raise_on_name_alphanumeric_dashes_only,
21
21
  raise_on_name_alphanumeric_underscores_only,
22
+ raise_on_name_with_banned_characters,
22
23
  remove_old_deployment_fields,
23
24
  return_none_schedule,
24
25
  validate_message_template_variables,
25
26
  validate_name_present_on_nonanonymous_blocks,
27
+ validate_schedule_max_scheduled_runs,
26
28
  )
27
29
  from prefect.client.schemas.objects import StateDetails, StateType
28
30
  from prefect.client.schemas.schedules import SCHEDULE_TYPES
29
- from prefect.types import NonNegativeInteger
31
+ from prefect.settings import PREFECT_DEPLOYMENT_SCHEDULE_MAX_SCHEDULED_RUNS
32
+ from prefect.types import NonNegativeFloat, NonNegativeInteger, PositiveInteger
30
33
  from prefect.utilities.collections import listrepr
31
34
  from prefect.utilities.pydantic import get_class_fields_only
32
35
 
@@ -101,6 +104,24 @@ class DeploymentScheduleCreate(ActionBaseModel):
101
104
  active: bool = Field(
102
105
  default=True, description="Whether or not the schedule is active."
103
106
  )
107
+ max_active_runs: Optional[PositiveInteger] = Field(
108
+ default=None,
109
+ description="The maximum number of active runs for the schedule.",
110
+ )
111
+ max_scheduled_runs: Optional[PositiveInteger] = Field(
112
+ default=None,
113
+ description="The maximum number of scheduled runs for the schedule.",
114
+ )
115
+ catchup: bool = Field(
116
+ default=False,
117
+ description="Whether or not a worker should catch up on Late runs for the schedule.",
118
+ )
119
+
120
+ @validator("max_scheduled_runs")
121
+ def validate_max_scheduled_runs(cls, v):
122
+ return validate_schedule_max_scheduled_runs(
123
+ v, PREFECT_DEPLOYMENT_SCHEDULE_MAX_SCHEDULED_RUNS.value()
124
+ )
104
125
 
105
126
 
106
127
  class DeploymentScheduleUpdate(ActionBaseModel):
@@ -111,6 +132,27 @@ class DeploymentScheduleUpdate(ActionBaseModel):
111
132
  default=True, description="Whether or not the schedule is active."
112
133
  )
113
134
 
135
+ max_active_runs: Optional[PositiveInteger] = Field(
136
+ default=None,
137
+ description="The maximum number of active runs for the schedule.",
138
+ )
139
+
140
+ max_scheduled_runs: Optional[PositiveInteger] = Field(
141
+ default=None,
142
+ description="The maximum number of scheduled runs for the schedule.",
143
+ )
144
+
145
+ catchup: Optional[bool] = Field(
146
+ default=None,
147
+ description="Whether or not a worker should catch up on Late runs for the schedule.",
148
+ )
149
+
150
+ @validator("max_scheduled_runs")
151
+ def validate_max_scheduled_runs(cls, v):
152
+ return validate_schedule_max_scheduled_runs(
153
+ v, PREFECT_DEPLOYMENT_SCHEDULE_MAX_SCHEDULED_RUNS.value()
154
+ )
155
+
114
156
 
115
157
  class DeploymentCreate(DeprecatedInfraOverridesField, ActionBaseModel):
116
158
  """Data used by the Prefect REST API to create a deployment."""
@@ -704,7 +746,7 @@ class GlobalConcurrencyLimitCreate(ActionBaseModel):
704
746
  """Data used by the Prefect REST API to create a global concurrency limit."""
705
747
 
706
748
  name: str = Field(description="The name of the global concurrency limit.")
707
- limit: int = Field(
749
+ limit: NonNegativeInteger = Field(
708
750
  description=(
709
751
  "The maximum number of slots that can be occupied on this concurrency"
710
752
  " limit."
@@ -714,11 +756,11 @@ class GlobalConcurrencyLimitCreate(ActionBaseModel):
714
756
  default=True,
715
757
  description="Whether or not the concurrency limit is in an active state.",
716
758
  )
717
- active_slots: Optional[int] = Field(
759
+ active_slots: Optional[NonNegativeInteger] = Field(
718
760
  default=0,
719
761
  description="Number of tasks currently using a concurrency slot.",
720
762
  )
721
- slot_decay_per_second: Optional[float] = Field(
763
+ slot_decay_per_second: Optional[NonNegativeFloat] = Field(
722
764
  default=0.0,
723
765
  description=(
724
766
  "Controls the rate at which slots are released when the concurrency limit"
@@ -726,12 +768,20 @@ class GlobalConcurrencyLimitCreate(ActionBaseModel):
726
768
  ),
727
769
  )
728
770
 
771
+ @validator("name", check_fields=False)
772
+ def validate_name_characters(cls, v):
773
+ return raise_on_name_with_banned_characters(v)
774
+
729
775
 
730
776
  class GlobalConcurrencyLimitUpdate(ActionBaseModel):
731
777
  """Data used by the Prefect REST API to update a global concurrency limit."""
732
778
 
733
779
  name: Optional[str] = Field(None)
734
- limit: Optional[int] = Field(None)
735
- active: Optional[bool] = Field(None)
736
- active_slots: Optional[int] = Field(None)
737
- slot_decay_per_second: Optional[float] = Field(None)
780
+ limit: Optional[NonNegativeInteger] = Field(None)
781
+ active: Optional[NonNegativeInteger] = Field(None)
782
+ active_slots: Optional[NonNegativeInteger] = Field(None)
783
+ slot_decay_per_second: Optional[NonNegativeFloat] = Field(None)
784
+
785
+ @validator("name", check_fields=False)
786
+ def validate_name_characters(cls, v):
787
+ return raise_on_name_with_banned_characters(v)
@@ -690,7 +690,9 @@ class TaskRun(ObjectBaseModel):
690
690
  task_inputs: Dict[str, List[Union[TaskRunResult, Parameter, Constant]]] = Field(
691
691
  default_factory=dict,
692
692
  description=(
693
- "Tracks the source of inputs to a task run. Used for internal bookkeeping."
693
+ "Tracks the source of inputs to a task run. Used for internal bookkeeping. "
694
+ "Note the special __parents__ key, used to indicate a parent/child "
695
+ "relationship that may or may not include an input or wait_for semantic."
694
696
  ),
695
697
  )
696
698
  state_type: Optional[StateType] = Field(
@@ -931,6 +933,18 @@ class DeploymentSchedule(ObjectBaseModel):
931
933
  active: bool = Field(
932
934
  default=True, description="Whether or not the schedule is active."
933
935
  )
936
+ max_active_runs: Optional[PositiveInteger] = Field(
937
+ default=None,
938
+ description="The maximum number of active runs for the schedule.",
939
+ )
940
+ max_scheduled_runs: Optional[PositiveInteger] = Field(
941
+ default=None,
942
+ description="The maximum number of scheduled runs for the schedule.",
943
+ )
944
+ catchup: bool = Field(
945
+ default=False,
946
+ description="Whether or not a worker should catch up on Late runs for the schedule.",
947
+ )
934
948
 
935
949
 
936
950
  class Deployment(DeprecatedInfraOverridesField, ObjectBaseModel):
@@ -429,3 +429,22 @@ class MinimalConcurrencyLimitResponse(PrefectBaseModel):
429
429
  id: UUID
430
430
  name: str
431
431
  limit: int
432
+
433
+
434
+ class GlobalConcurrencyLimitResponse(ObjectBaseModel):
435
+ """
436
+ A response object for global concurrency limits.
437
+ """
438
+
439
+ active: bool = Field(
440
+ default=True, description="Whether the global concurrency limit is active."
441
+ )
442
+ name: str = Field(
443
+ default=..., description="The name of the global concurrency limit."
444
+ )
445
+ limit: int = Field(default=..., description="The concurrency limit.")
446
+ active_slots: int = Field(default=..., description="The number of active slots.")
447
+ slot_decay_per_second: float = Field(
448
+ default=2.0,
449
+ description="The decay rate for active slots when used as a rate limit.",
450
+ )
@@ -65,7 +65,7 @@ class IntervalSchedule(PrefectBaseModel):
65
65
  exclude_none = True
66
66
 
67
67
  interval: PositiveDuration
68
- anchor_date: DateTimeTZ = None
68
+ anchor_date: Optional[DateTimeTZ] = None
69
69
  timezone: Optional[str] = Field(default=None, examples=["America/New_York"])
70
70
 
71
71
  @validator("anchor_date", always=True)
@@ -49,12 +49,12 @@ def get_or_create_client(
49
49
 
50
50
  if (
51
51
  flow_run_context
52
- and getattr(flow_run_context.client, "_loop") == get_running_loop()
52
+ and getattr(flow_run_context.client, "_loop", None) == get_running_loop()
53
53
  ):
54
54
  return flow_run_context.client, True
55
55
  elif (
56
56
  task_run_context
57
- and getattr(task_run_context.client, "_loop") == get_running_loop()
57
+ and getattr(task_run_context.client, "_loop", None) == get_running_loop()
58
58
  ):
59
59
  return task_run_context.client, True
60
60
  else:
@@ -1,6 +1,6 @@
1
1
  import asyncio
2
2
  from contextlib import asynccontextmanager
3
- from typing import List, Literal, Union, cast
3
+ from typing import List, Literal, Optional, Union, cast
4
4
 
5
5
  import httpx
6
6
  import pendulum
@@ -13,6 +13,7 @@ except ImportError:
13
13
 
14
14
  from prefect import get_client
15
15
  from prefect.client.schemas.responses import MinimalConcurrencyLimitResponse
16
+ from prefect.utilities.timeout import timeout_async
16
17
 
17
18
  from .events import (
18
19
  _emit_concurrency_acquisition_events,
@@ -26,10 +27,39 @@ class ConcurrencySlotAcquisitionError(Exception):
26
27
 
27
28
 
28
29
  @asynccontextmanager
29
- async def concurrency(names: Union[str, List[str]], occupy: int = 1):
30
- names = names if isinstance(names, list) else [names]
30
+ async def concurrency(
31
+ names: Union[str, List[str]],
32
+ occupy: int = 1,
33
+ timeout_seconds: Optional[float] = None,
34
+ ):
35
+ """A context manager that acquires and releases concurrency slots from the
36
+ given concurrency limits.
37
+
38
+ Args:
39
+ names: The names of the concurrency limits to acquire slots from.
40
+ occupy: The number of slots to acquire and hold from each limit.
41
+ timeout_seconds: The number of seconds to wait for the slots to be acquired before
42
+ raising a `TimeoutError`. A timeout of `None` will wait indefinitely.
31
43
 
32
- limits = await _acquire_concurrency_slots(names, occupy)
44
+ Raises:
45
+ TimeoutError: If the slots are not acquired within the given timeout.
46
+
47
+ Example:
48
+ A simple example of using the async `concurrency` context manager:
49
+ ```python
50
+ from prefect.concurrency.asyncio import concurrency
51
+
52
+ async def resource_heavy():
53
+ async with concurrency("test", occupy=1):
54
+ print("Resource heavy task")
55
+
56
+ async def main():
57
+ await resource_heavy()
58
+ ```
59
+ """
60
+ names = names if isinstance(names, list) else [names]
61
+ with timeout_async(seconds=timeout_seconds):
62
+ limits = await _acquire_concurrency_slots(names, occupy)
33
63
  acquisition_time = pendulum.now("UTC")
34
64
  emitted_events = _emit_concurrency_acquisition_events(limits, occupy)
35
65
 
@@ -1,5 +1,5 @@
1
1
  from contextlib import contextmanager
2
- from typing import List, Union, cast
2
+ from typing import List, Optional, Union, cast
3
3
 
4
4
  import pendulum
5
5
 
@@ -12,6 +12,7 @@ except ImportError:
12
12
  from prefect._internal.concurrency.api import create_call, from_sync
13
13
  from prefect._internal.concurrency.event_loop import get_running_loop
14
14
  from prefect.client.schemas.responses import MinimalConcurrencyLimitResponse
15
+ from prefect.utilities.timeout import timeout
15
16
 
16
17
  from .asyncio import (
17
18
  _acquire_concurrency_slots,
@@ -24,12 +25,42 @@ from .events import (
24
25
 
25
26
 
26
27
  @contextmanager
27
- def concurrency(names: Union[str, List[str]], occupy: int = 1):
28
+ def concurrency(
29
+ names: Union[str, List[str]],
30
+ occupy: int = 1,
31
+ timeout_seconds: Optional[float] = None,
32
+ ):
33
+ """A context manager that acquires and releases concurrency slots from the
34
+ given concurrency limits.
35
+
36
+ Args:
37
+ names: The names of the concurrency limits to acquire slots from.
38
+ occupy: The number of slots to acquire and hold from each limit.
39
+ timeout_seconds: The number of seconds to wait for the slots to be acquired before
40
+ raising a `TimeoutError`. A timeout of `None` will wait indefinitely.
41
+
42
+ Raises:
43
+ TimeoutError: If the slots are not acquired within the given timeout.
44
+
45
+ Example:
46
+ A simple example of using the sync `concurrency` context manager:
47
+ ```python
48
+ from prefect.concurrency.sync import concurrency
49
+
50
+ def resource_heavy():
51
+ with concurrency("test", occupy=1):
52
+ print("Resource heavy task")
53
+
54
+ def main():
55
+ resource_heavy()
56
+ ```
57
+ """
28
58
  names = names if isinstance(names, list) else [names]
29
59
 
30
- limits: List[MinimalConcurrencyLimitResponse] = _call_async_function_from_sync(
31
- _acquire_concurrency_slots, names, occupy
32
- )
60
+ with timeout(seconds=timeout_seconds):
61
+ limits: List[MinimalConcurrencyLimitResponse] = _call_async_function_from_sync(
62
+ _acquire_concurrency_slots, names, occupy
63
+ )
33
64
  acquisition_time = pendulum.now("UTC")
34
65
  emitted_events = _emit_concurrency_acquisition_events(limits, occupy)
35
66
 
@@ -38,7 +69,10 @@ def concurrency(names: Union[str, List[str]], occupy: int = 1):
38
69
  finally:
39
70
  occupancy_period = cast(Interval, pendulum.now("UTC") - acquisition_time)
40
71
  _call_async_function_from_sync(
41
- _release_concurrency_slots, names, occupy, occupancy_period.total_seconds()
72
+ _release_concurrency_slots,
73
+ names,
74
+ occupy,
75
+ occupancy_period.total_seconds(),
42
76
  )
43
77
  _emit_concurrency_release_events(limits, occupy, emitted_events)
44
78
 
prefect/context.py CHANGED
@@ -43,7 +43,7 @@ import prefect.logging
43
43
  import prefect.logging.configuration
44
44
  import prefect.settings
45
45
  from prefect._internal.schemas.fields import DateTimeTZ
46
- from prefect.client.orchestration import PrefectClient
46
+ from prefect.client.orchestration import PrefectClient, SyncPrefectClient
47
47
  from prefect.client.schemas import FlowRun, TaskRun
48
48
  from prefect.events.worker import EventsWorker
49
49
  from prefect.exceptions import MissingContextError
@@ -213,7 +213,7 @@ class RunContext(ContextModel):
213
213
 
214
214
  start_time: DateTimeTZ = Field(default_factory=lambda: pendulum.now("UTC"))
215
215
  input_keyset: Optional[Dict[str, Dict[str, str]]] = None
216
- client: PrefectClient
216
+ client: Union[PrefectClient, SyncPrefectClient]
217
217
 
218
218
 
219
219
  class EngineContext(RunContext):
prefect/engine.py CHANGED
@@ -2438,14 +2438,14 @@ if __name__ == "__main__":
2438
2438
  if PREFECT_EXPERIMENTAL_ENABLE_NEW_ENGINE.value():
2439
2439
  from prefect.new_flow_engine import (
2440
2440
  load_flow_and_flow_run,
2441
- run_flow,
2441
+ run_flow_async,
2442
2442
  run_flow_sync,
2443
2443
  )
2444
2444
 
2445
2445
  flow_run, flow = run_sync(load_flow_and_flow_run)
2446
2446
  # run the flow
2447
2447
  if flow.isasync:
2448
- run_sync(run_flow(flow, flow_run=flow_run))
2448
+ run_sync(run_flow_async(flow, flow_run=flow_run))
2449
2449
  else:
2450
2450
  run_flow_sync(flow, flow_run=flow_run)
2451
2451
  else:
prefect/events/clients.py CHANGED
@@ -28,7 +28,7 @@ from websockets.exceptions import (
28
28
  ConnectionClosedOK,
29
29
  )
30
30
 
31
- from prefect.client.base import PrefectHttpxClient
31
+ from prefect.client.base import PrefectHttpxAsyncClient
32
32
  from prefect.events import Event
33
33
  from prefect.logging import get_logger
34
34
  from prefect.settings import (
@@ -193,7 +193,7 @@ class PrefectEphemeralEventsClient(EventsClient):
193
193
 
194
194
  app = create_app()
195
195
 
196
- self._http_client = PrefectHttpxClient(
196
+ self._http_client = PrefectHttpxAsyncClient(
197
197
  transport=httpx.ASGITransport(app=app, raise_app_exceptions=False),
198
198
  base_url="http://ephemeral-prefect/api",
199
199
  enable_csrf_support=False,
prefect/flows.py CHANGED
@@ -343,23 +343,6 @@ class Flow(Generic[P, R]):
343
343
  self.result_storage = result_storage
344
344
  self.result_serializer = result_serializer
345
345
  self.cache_result_in_memory = cache_result_in_memory
346
-
347
- # Check for collision in the registry
348
- registry = PrefectObjectRegistry.get()
349
-
350
- if registry and any(
351
- other
352
- for other in registry.get_instances(Flow)
353
- if other.name == self.name and id(other.fn) != id(self.fn)
354
- ):
355
- file = inspect.getsourcefile(self.fn)
356
- line_number = inspect.getsourcelines(self.fn)[1]
357
- warnings.warn(
358
- f"A flow named {self.name!r} and defined at '{file}:{line_number}' "
359
- "conflicts with another flow. Consider specifying a unique `name` "
360
- "parameter in the flow definition:\n\n "
361
- "`@flow(name='my_unique_name', ...)`"
362
- )
363
346
  self.on_completion = on_completion
364
347
  self.on_failure = on_failure
365
348
  self.on_cancellation = on_cancellation
@@ -1719,3 +1702,94 @@ def load_flow_from_text(script_contents: AnyStr, flow_name: str):
1719
1702
  tmpfile.close()
1720
1703
  os.remove(tmpfile.name)
1721
1704
  return flow
1705
+
1706
+
1707
+ @sync_compatible
1708
+ async def serve(
1709
+ *args: "RunnerDeployment",
1710
+ pause_on_shutdown: bool = True,
1711
+ print_starting_message: bool = True,
1712
+ limit: Optional[int] = None,
1713
+ **kwargs,
1714
+ ):
1715
+ """
1716
+ Serve the provided list of deployments.
1717
+
1718
+ Args:
1719
+ *args: A list of deployments to serve.
1720
+ pause_on_shutdown: A boolean for whether or not to automatically pause
1721
+ deployment schedules on shutdown.
1722
+ print_starting_message: Whether or not to print message to the console
1723
+ on startup.
1724
+ limit: The maximum number of runs that can be executed concurrently.
1725
+ **kwargs: Additional keyword arguments to pass to the runner.
1726
+
1727
+ Examples:
1728
+ Prepare two deployments and serve them:
1729
+
1730
+ ```python
1731
+ import datetime
1732
+
1733
+ from prefect import flow, serve
1734
+
1735
+ @flow
1736
+ def my_flow(name):
1737
+ print(f"hello {name}")
1738
+
1739
+ @flow
1740
+ def my_other_flow(name):
1741
+ print(f"goodbye {name}")
1742
+
1743
+ if __name__ == "__main__":
1744
+ # Run once a day
1745
+ hello_deploy = my_flow.to_deployment(
1746
+ "hello", tags=["dev"], interval=datetime.timedelta(days=1)
1747
+ )
1748
+
1749
+ # Run every Sunday at 4:00 AM
1750
+ bye_deploy = my_other_flow.to_deployment(
1751
+ "goodbye", tags=["dev"], cron="0 4 * * sun"
1752
+ )
1753
+
1754
+ serve(hello_deploy, bye_deploy)
1755
+ ```
1756
+ """
1757
+ from rich.console import Console, Group
1758
+ from rich.table import Table
1759
+
1760
+ from prefect.runner import Runner
1761
+
1762
+ runner = Runner(pause_on_shutdown=pause_on_shutdown, limit=limit, **kwargs)
1763
+ for deployment in args:
1764
+ await runner.add_deployment(deployment)
1765
+
1766
+ if print_starting_message:
1767
+ help_message_top = (
1768
+ "[green]Your deployments are being served and polling for"
1769
+ " scheduled runs!\n[/]"
1770
+ )
1771
+
1772
+ table = Table(title="Deployments", show_header=False)
1773
+
1774
+ table.add_column(style="blue", no_wrap=True)
1775
+
1776
+ for deployment in args:
1777
+ table.add_row(f"{deployment.flow_name}/{deployment.name}")
1778
+
1779
+ help_message_bottom = (
1780
+ "\nTo trigger any of these deployments, use the"
1781
+ " following command:\n[blue]\n\t$ prefect deployment run"
1782
+ " [DEPLOYMENT_NAME]\n[/]"
1783
+ )
1784
+ if PREFECT_UI_URL:
1785
+ help_message_bottom += (
1786
+ "\nYou can also trigger your deployments via the Prefect UI:"
1787
+ f" [blue]{PREFECT_UI_URL.value()}/deployments[/]\n"
1788
+ )
1789
+
1790
+ console = Console()
1791
+ console.print(
1792
+ Group(help_message_top, table, help_message_bottom), soft_wrap=True
1793
+ )
1794
+
1795
+ await runner.start()
@@ -7,7 +7,6 @@ It has been replaced by the process worker from the `prefect.workers` module, wh
7
7
  For upgrade instructions, see https://docs.prefect.io/latest/guides/upgrade-guide-agents-to-workers/.
8
8
  """
9
9
 
10
- import asyncio
11
10
  import contextlib
12
11
  import os
13
12
  import shlex
@@ -21,7 +20,6 @@ from typing import Dict, Tuple, Union
21
20
 
22
21
  import anyio
23
22
  import anyio.abc
24
- import sniffio
25
23
 
26
24
  from prefect._internal.compatibility.deprecated import deprecated_class
27
25
  from prefect._internal.pydantic import HAS_PYDANTIC_V2
@@ -43,20 +41,6 @@ if sys.platform == "win32":
43
41
  STATUS_CONTROL_C_EXIT = 0xC000013A
44
42
 
45
43
 
46
- def _use_threaded_child_watcher():
47
- if (
48
- sys.version_info < (3, 8)
49
- and sniffio.current_async_library() == "asyncio"
50
- and sys.platform != "win32"
51
- ):
52
- from prefect.utilities.compat import ThreadedChildWatcher
53
-
54
- # Python < 3.8 does not use a `ThreadedChildWatcher` by default which can
55
- # lead to errors in tests on unix as the previous default `SafeChildWatcher`
56
- # is not compatible with threaded event loops.
57
- asyncio.get_event_loop_policy().set_child_watcher(ThreadedChildWatcher())
58
-
59
-
60
44
  def _infrastructure_pid_from_process(process: anyio.abc.Process) -> str:
61
45
  hostname = socket.gethostname()
62
46
  return f"{hostname}:{process.pid}"
@@ -121,7 +105,6 @@ class Process(Infrastructure):
121
105
  if not self.command:
122
106
  raise ValueError("Process cannot be run with empty command.")
123
107
 
124
- _use_threaded_child_watcher()
125
108
  display_name = f" {self.name!r}" if self.name else ""
126
109
 
127
110
  # Open a subprocess to execute the flow run
@@ -99,10 +99,7 @@ class PrefectFormatter(logging.Formatter):
99
99
  style_kwargs["defaults"] = defaults
100
100
 
101
101
  # validate added in 3.8
102
- if sys.version_info >= (3, 8):
103
- init_kwargs["validate"] = validate
104
- else:
105
- validate = False
102
+ init_kwargs["validate"] = validate
106
103
 
107
104
  super().__init__(format, datefmt, style, **init_kwargs)
108
105