prefect-client 3.1.12__py3-none-any.whl → 3.1.13__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 (105) hide show
  1. prefect/_experimental/sla/client.py +53 -27
  2. prefect/_experimental/sla/objects.py +10 -2
  3. prefect/_internal/concurrency/services.py +2 -2
  4. prefect/_internal/concurrency/threads.py +6 -0
  5. prefect/_internal/retries.py +6 -3
  6. prefect/_internal/schemas/validators.py +6 -4
  7. prefect/_version.py +3 -3
  8. prefect/artifacts.py +4 -1
  9. prefect/automations.py +1 -1
  10. prefect/blocks/abstract.py +5 -2
  11. prefect/blocks/notifications.py +1 -0
  12. prefect/cache_policies.py +20 -20
  13. prefect/client/utilities.py +3 -3
  14. prefect/deployments/base.py +7 -4
  15. prefect/deployments/flow_runs.py +5 -1
  16. prefect/deployments/runner.py +6 -11
  17. prefect/deployments/steps/core.py +1 -1
  18. prefect/deployments/steps/pull.py +8 -3
  19. prefect/deployments/steps/utility.py +2 -2
  20. prefect/docker/docker_image.py +13 -9
  21. prefect/engine.py +19 -10
  22. prefect/events/cli/automations.py +4 -4
  23. prefect/events/clients.py +17 -14
  24. prefect/events/schemas/automations.py +12 -8
  25. prefect/events/schemas/events.py +5 -1
  26. prefect/events/worker.py +1 -1
  27. prefect/filesystems.py +1 -1
  28. prefect/flow_engine.py +17 -9
  29. prefect/flows.py +118 -73
  30. prefect/futures.py +14 -7
  31. prefect/infrastructure/provisioners/__init__.py +2 -0
  32. prefect/infrastructure/provisioners/cloud_run.py +4 -4
  33. prefect/infrastructure/provisioners/coiled.py +249 -0
  34. prefect/infrastructure/provisioners/container_instance.py +4 -3
  35. prefect/infrastructure/provisioners/ecs.py +55 -43
  36. prefect/infrastructure/provisioners/modal.py +5 -4
  37. prefect/input/actions.py +5 -1
  38. prefect/input/run_input.py +157 -43
  39. prefect/logging/configuration.py +3 -3
  40. prefect/logging/filters.py +2 -2
  41. prefect/logging/formatters.py +15 -11
  42. prefect/logging/handlers.py +24 -14
  43. prefect/logging/highlighters.py +5 -5
  44. prefect/logging/loggers.py +28 -18
  45. prefect/main.py +3 -1
  46. prefect/results.py +166 -86
  47. prefect/runner/runner.py +34 -27
  48. prefect/runner/server.py +3 -1
  49. prefect/runner/storage.py +18 -18
  50. prefect/runner/submit.py +19 -12
  51. prefect/runtime/deployment.py +15 -8
  52. prefect/runtime/flow_run.py +19 -6
  53. prefect/runtime/task_run.py +7 -3
  54. prefect/settings/base.py +17 -7
  55. prefect/settings/legacy.py +4 -4
  56. prefect/settings/models/api.py +4 -3
  57. prefect/settings/models/cli.py +4 -3
  58. prefect/settings/models/client.py +7 -4
  59. prefect/settings/models/cloud.py +4 -3
  60. prefect/settings/models/deployments.py +4 -3
  61. prefect/settings/models/experiments.py +4 -3
  62. prefect/settings/models/flows.py +4 -3
  63. prefect/settings/models/internal.py +4 -3
  64. prefect/settings/models/logging.py +8 -6
  65. prefect/settings/models/results.py +4 -3
  66. prefect/settings/models/root.py +11 -16
  67. prefect/settings/models/runner.py +8 -5
  68. prefect/settings/models/server/api.py +6 -3
  69. prefect/settings/models/server/database.py +120 -25
  70. prefect/settings/models/server/deployments.py +4 -3
  71. prefect/settings/models/server/ephemeral.py +7 -4
  72. prefect/settings/models/server/events.py +6 -3
  73. prefect/settings/models/server/flow_run_graph.py +4 -3
  74. prefect/settings/models/server/root.py +4 -3
  75. prefect/settings/models/server/services.py +15 -12
  76. prefect/settings/models/server/tasks.py +7 -4
  77. prefect/settings/models/server/ui.py +4 -3
  78. prefect/settings/models/tasks.py +10 -5
  79. prefect/settings/models/testing.py +4 -3
  80. prefect/settings/models/worker.py +7 -4
  81. prefect/settings/profiles.py +13 -12
  82. prefect/settings/sources.py +20 -19
  83. prefect/states.py +17 -13
  84. prefect/task_engine.py +43 -33
  85. prefect/task_runners.py +35 -23
  86. prefect/task_runs.py +20 -11
  87. prefect/task_worker.py +12 -7
  88. prefect/tasks.py +30 -24
  89. prefect/telemetry/bootstrap.py +4 -1
  90. prefect/telemetry/run_telemetry.py +15 -13
  91. prefect/transactions.py +3 -3
  92. prefect/types/__init__.py +3 -1
  93. prefect/utilities/_deprecated.py +38 -0
  94. prefect/utilities/engine.py +11 -4
  95. prefect/utilities/filesystem.py +2 -2
  96. prefect/utilities/generics.py +1 -1
  97. prefect/utilities/pydantic.py +21 -36
  98. prefect/workers/base.py +52 -30
  99. prefect/workers/process.py +20 -15
  100. prefect/workers/server.py +4 -5
  101. {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/METADATA +2 -2
  102. {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/RECORD +105 -103
  103. {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/LICENSE +0 -0
  104. {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/WHEEL +0 -0
  105. {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/top_level.txt +0 -0
prefect/tasks.py CHANGED
@@ -53,10 +53,7 @@ from prefect.results import (
53
53
  ResultStore,
54
54
  get_or_create_default_task_scheduling_storage,
55
55
  )
56
- from prefect.settings import (
57
- PREFECT_TASK_DEFAULT_RETRIES,
58
- PREFECT_TASK_DEFAULT_RETRY_DELAY_SECONDS,
59
- )
56
+ from prefect.settings.context import get_current_settings
60
57
  from prefect.states import Pending, Scheduled, State
61
58
  from prefect.utilities.annotations import NotSet
62
59
  from prefect.utilities.asyncutils import run_coro_as_sync, sync_compatible
@@ -70,6 +67,8 @@ from prefect.utilities.importtools import to_qualified_name
70
67
  from prefect.utilities.urls import url_for
71
68
 
72
69
  if TYPE_CHECKING:
70
+ import logging
71
+
73
72
  from prefect.client.orchestration import PrefectClient
74
73
  from prefect.context import TaskRunContext
75
74
  from prefect.transactions import Transaction
@@ -80,7 +79,7 @@ P = ParamSpec("P") # The parameters of the task
80
79
 
81
80
  NUM_CHARS_DYNAMIC_KEY = 8
82
81
 
83
- logger = get_logger("tasks")
82
+ logger: "logging.Logger" = get_logger("tasks")
84
83
 
85
84
  FutureOrResult: TypeAlias = Union[PrefectFuture[T], T]
86
85
  OneOrManyFutureOrResult: TypeAlias = Union[
@@ -383,19 +382,19 @@ class Task(Generic[P, R]):
383
382
  if not callable(fn):
384
383
  raise TypeError("'fn' must be callable")
385
384
 
386
- self.description = description or inspect.getdoc(fn)
385
+ self.description: str | None = description or inspect.getdoc(fn)
387
386
  update_wrapper(self, fn)
388
387
  self.fn = fn
389
388
 
390
389
  # the task is considered async if its function is async or an async
391
390
  # generator
392
- self.isasync = asyncio.iscoroutinefunction(
391
+ self.isasync: bool = asyncio.iscoroutinefunction(
393
392
  self.fn
394
393
  ) or inspect.isasyncgenfunction(self.fn)
395
394
 
396
395
  # the task is considered a generator if its function is a generator or
397
396
  # an async generator
398
- self.isgenerator = inspect.isgeneratorfunction(
397
+ self.isgenerator: bool = inspect.isgeneratorfunction(
399
398
  self.fn
400
399
  ) or inspect.isasyncgenfunction(self.fn)
401
400
 
@@ -405,7 +404,7 @@ class Task(Generic[P, R]):
405
404
  else:
406
405
  self.name = self.fn.__name__
407
406
  else:
408
- self.name = name
407
+ self.name: str = name
409
408
 
410
409
  if task_run_name is not None:
411
410
  if not isinstance(task_run_name, str) and not callable(task_run_name):
@@ -420,9 +419,9 @@ class Task(Generic[P, R]):
420
419
 
421
420
  raise_for_reserved_arguments(self.fn, ["return_state", "wait_for"])
422
421
 
423
- self.tags = set(tags if tags else [])
422
+ self.tags: set[str] = set(tags if tags else [])
424
423
 
425
- self.task_key = _generate_task_key(self.fn)
424
+ self.task_key: str = _generate_task_key(self.fn)
426
425
 
427
426
  if cache_policy is not NotSet and cache_key_fn is not None:
428
427
  logger.warning(
@@ -464,26 +463,29 @@ class Task(Generic[P, R]):
464
463
  # TODO: handle this situation with double storage
465
464
  self.cache_policy = None
466
465
  else:
467
- self.cache_policy = cache_policy
466
+ self.cache_policy: Union[CachePolicy, type[NotSet], None] = cache_policy
468
467
 
469
468
  # TaskRunPolicy settings
470
469
  # TODO: We can instantiate a `TaskRunPolicy` and add Pydantic bound checks to
471
470
  # validate that the user passes positive numbers here
472
471
 
473
- self.retries = (
474
- retries if retries is not None else PREFECT_TASK_DEFAULT_RETRIES.value()
472
+ settings = get_current_settings()
473
+ self.retries: int = (
474
+ retries if retries is not None else settings.tasks.default_retries
475
475
  )
476
476
  if retry_delay_seconds is None:
477
- retry_delay_seconds = PREFECT_TASK_DEFAULT_RETRY_DELAY_SECONDS.value()
477
+ retry_delay_seconds = settings.tasks.default_retry_delay_seconds
478
478
 
479
479
  if callable(retry_delay_seconds):
480
- self.retry_delay_seconds = retry_delay_seconds(retries)
480
+ self.retry_delay_seconds = retry_delay_seconds(self.retries)
481
481
  elif not isinstance(retry_delay_seconds, (list, int, float, type(None))):
482
482
  raise TypeError(
483
483
  f"Invalid `retry_delay_seconds` provided; must be an int, float, list or callable. Received type {type(retry_delay_seconds)}"
484
484
  )
485
485
  else:
486
- self.retry_delay_seconds = retry_delay_seconds
486
+ self.retry_delay_seconds: Union[
487
+ float, int, list[float], None
488
+ ] = retry_delay_seconds
487
489
 
488
490
  if isinstance(self.retry_delay_seconds, list) and (
489
491
  len(self.retry_delay_seconds) > 50
@@ -507,11 +509,15 @@ class Task(Generic[P, R]):
507
509
  self.result_serializer = result_serializer
508
510
  self.result_storage_key = result_storage_key
509
511
  self.cache_result_in_memory = cache_result_in_memory
510
- self.timeout_seconds = float(timeout_seconds) if timeout_seconds else None
511
- self.on_rollback_hooks = on_rollback or []
512
- self.on_commit_hooks = on_commit or []
513
- self.on_completion_hooks = on_completion or []
514
- self.on_failure_hooks = on_failure or []
512
+ self.timeout_seconds: Union[float, None] = (
513
+ float(timeout_seconds) if timeout_seconds else None
514
+ )
515
+ self.on_rollback_hooks: list[Callable[["Transaction"], None]] = (
516
+ on_rollback or []
517
+ )
518
+ self.on_commit_hooks: list[Callable[["Transaction"], None]] = on_commit or []
519
+ self.on_completion_hooks: list[StateHookCallable] = on_completion or []
520
+ self.on_failure_hooks: list[StateHookCallable] = on_failure or []
515
521
 
516
522
  # retry_condition_fn must be a callable or None. If it is neither, raise a TypeError
517
523
  if retry_condition_fn is not None and not (callable(retry_condition_fn)):
@@ -527,7 +533,7 @@ class Task(Generic[P, R]):
527
533
  def ismethod(self) -> bool:
528
534
  return hasattr(self.fn, "__prefect_self__")
529
535
 
530
- def __get__(self, instance: Any, owner: Any):
536
+ def __get__(self, instance: Any, owner: Any) -> "Task[P, R]":
531
537
  """
532
538
  Implement the descriptor protocol so that the task can be used as an instance method.
533
539
  When an instance method is loaded, this method is called with the "self" instance as
@@ -582,7 +588,7 @@ class Task(Generic[P, R]):
582
588
  Callable[["Task[..., Any]", TaskRun, State], bool]
583
589
  ] = None,
584
590
  viz_return_value: Optional[Any] = None,
585
- ):
591
+ ) -> "Task[P, R]":
586
592
  """
587
593
  Create a new task from the current object, updating provided options.
588
594
 
@@ -4,7 +4,10 @@ import prefect.settings
4
4
  from prefect.client.base import ServerType, determine_server_type
5
5
  from prefect.logging.loggers import get_logger
6
6
 
7
- logger = get_logger(__name__)
7
+ if TYPE_CHECKING:
8
+ import logging
9
+
10
+ logger: "logging.Logger" = get_logger(__name__)
8
11
 
9
12
  if TYPE_CHECKING:
10
13
  from opentelemetry.sdk._logs import LoggerProvider
@@ -1,6 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  import time
2
4
  from dataclasses import dataclass, field
3
- from typing import TYPE_CHECKING, Any, Optional, Union
5
+ from typing import TYPE_CHECKING, Any, Union
4
6
 
5
7
  from opentelemetry import propagate, trace
6
8
  from opentelemetry.context import Context
@@ -47,14 +49,14 @@ class RunTelemetry:
47
49
  _tracer: "Tracer" = field(
48
50
  default_factory=lambda: get_tracer("prefect", prefect.__version__)
49
51
  )
50
- span: Optional[Span] = None
52
+ span: Span | None = None
51
53
 
52
54
  async def async_start_span(
53
55
  self,
54
56
  run: FlowOrTaskRun,
55
57
  client: PrefectClient,
56
- parameters: Optional[dict[str, Any]] = None,
57
- ):
58
+ parameters: dict[str, Any] | None = None,
59
+ ) -> Span:
58
60
  traceparent, span = self._start_span(run, parameters)
59
61
 
60
62
  if self._run_type(run) == "flow" and traceparent:
@@ -70,8 +72,8 @@ class RunTelemetry:
70
72
  self,
71
73
  run: FlowOrTaskRun,
72
74
  client: SyncPrefectClient,
73
- parameters: Optional[dict[str, Any]] = None,
74
- ):
75
+ parameters: dict[str, Any] | None = None,
76
+ ) -> Span:
75
77
  traceparent, span = self._start_span(run, parameters)
76
78
 
77
79
  if self._run_type(run) == "flow" and traceparent:
@@ -84,8 +86,8 @@ class RunTelemetry:
84
86
  def _start_span(
85
87
  self,
86
88
  run: FlowOrTaskRun,
87
- parameters: Optional[dict[str, Any]] = None,
88
- ) -> tuple[Optional[str], Span]:
89
+ parameters: dict[str, Any] | None = None,
90
+ ) -> tuple[str | None, Span]:
89
91
  """
90
92
  Start a span for a run.
91
93
  """
@@ -139,8 +141,8 @@ class RunTelemetry:
139
141
  return "task" if isinstance(run, TaskRun) else "flow"
140
142
 
141
143
  def _trace_context_from_labels(
142
- self, labels: Optional[KeyValueLabels]
143
- ) -> Optional[Context]:
144
+ self, labels: KeyValueLabels | None
145
+ ) -> Context | None:
144
146
  """Get trace context from run labels if it exists."""
145
147
  if not labels or LABELS_TRACEPARENT_KEY not in labels:
146
148
  return None
@@ -148,7 +150,7 @@ class RunTelemetry:
148
150
  carrier = {TRACEPARENT_KEY: traceparent}
149
151
  return propagate.extract(carrier)
150
152
 
151
- def _traceparent_from_span(self, span: Span) -> Optional[str]:
153
+ def _traceparent_from_span(self, span: Span) -> str | None:
152
154
  carrier: dict[str, Any] = {}
153
155
  propagate.inject(carrier, context=trace.set_span_in_context(span))
154
156
  return carrier.get(TRACEPARENT_KEY)
@@ -162,7 +164,7 @@ class RunTelemetry:
162
164
  self.span.end(time.time_ns())
163
165
  self.span = None
164
166
 
165
- def end_span_on_failure(self, terminal_message: Optional[str] = None) -> None:
167
+ def end_span_on_failure(self, terminal_message: str | None = None) -> None:
166
168
  """
167
169
  End a span for a run on failure.
168
170
  """
@@ -203,7 +205,7 @@ class RunTelemetry:
203
205
  self.span.update_name(name=name)
204
206
  self.span.set_attribute("prefect.run.name", name)
205
207
 
206
- def _parent_run(self) -> Union[FlowOrTaskRun, None]:
208
+ def _parent_run(self) -> FlowOrTaskRun | None:
207
209
  """
208
210
  Identify the "parent run" for the current execution context.
209
211
 
prefect/transactions.py CHANGED
@@ -173,7 +173,7 @@ class Transaction(ContextModel):
173
173
  def is_active(self) -> bool:
174
174
  return self.state == TransactionState.ACTIVE
175
175
 
176
- def __enter__(self):
176
+ def __enter__(self) -> Self:
177
177
  if self._token is not None:
178
178
  raise RuntimeError(
179
179
  "Context already entered. Context enter calls cannot be nested."
@@ -206,7 +206,7 @@ class Transaction(ContextModel):
206
206
  self._token = self.__var__.set(self)
207
207
  return self
208
208
 
209
- def __exit__(self, *exc_info: Any):
209
+ def __exit__(self, *exc_info: Any) -> None:
210
210
  exc_type, exc_val, _ = exc_info
211
211
  if not self._token:
212
212
  raise RuntimeError(
@@ -235,7 +235,7 @@ class Transaction(ContextModel):
235
235
 
236
236
  self.reset()
237
237
 
238
- def begin(self):
238
+ def begin(self) -> None:
239
239
  if (
240
240
  self.store
241
241
  and self.key
prefect/types/__init__.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from functools import partial
2
4
  from typing import Annotated, Any, Dict, List, Optional, Set, TypeVar, Union
3
5
  from typing_extensions import Literal, TypeAlias
@@ -114,7 +116,7 @@ class SecretDict(pydantic.Secret[Dict[str, Any]]):
114
116
 
115
117
 
116
118
  def validate_set_T_from_delim_string(
117
- value: Union[str, T, Set[T], None], type_, delim=None
119
+ value: Union[str, T, Set[T], None], type_: type[T], delim: str | None = None
118
120
  ) -> Set[T]:
119
121
  """
120
122
  "no-info" before validator useful in scooping env vars
@@ -0,0 +1,38 @@
1
+ from typing import Any
2
+
3
+ from jsonpatch import ( # type: ignore # no typing stubs available, see https://github.com/stefankoegl/python-json-patch/issues/158
4
+ JsonPatch as JsonPatchBase,
5
+ )
6
+ from pydantic import GetJsonSchemaHandler
7
+ from pydantic.json_schema import JsonSchemaValue
8
+ from pydantic_core import core_schema
9
+
10
+
11
+ class JsonPatch(JsonPatchBase):
12
+ @classmethod
13
+ def __get_pydantic_core_schema__(
14
+ cls, source_type: Any, handler: GetJsonSchemaHandler
15
+ ) -> core_schema.CoreSchema:
16
+ return core_schema.typed_dict_schema(
17
+ {"patch": core_schema.typed_dict_field(core_schema.dict_schema())}
18
+ )
19
+
20
+ @classmethod
21
+ def __get_pydantic_json_schema__(
22
+ cls, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
23
+ ) -> JsonSchemaValue:
24
+ json_schema = handler(core_schema)
25
+ json_schema = handler.resolve_ref_schema(json_schema)
26
+ json_schema.pop("required", None)
27
+ json_schema.pop("properties", None)
28
+ json_schema.update(
29
+ {
30
+ "type": "array",
31
+ "format": "rfc6902",
32
+ "items": {
33
+ "type": "object",
34
+ "additionalProperties": {"type": "string"},
35
+ },
36
+ }
37
+ )
38
+ return json_schema
@@ -757,12 +757,19 @@ def resolve_to_final_result(expr: Any, context: dict[str, Any]) -> Any:
757
757
  parameter_context = propagate.extract(
758
758
  {"traceparent": state.state_details.traceparent}
759
759
  )
760
- trace.get_current_span().add_link(
761
- context=trace.get_current_span(parameter_context).get_span_context(),
762
- attributes={
760
+ attributes = {}
761
+
762
+ # If this future is being used as a parameter (as opposed to just a wait_for),
763
+ # add attributes to the span to indicate the parameter name and type
764
+ if "parameter_name" in context:
765
+ attributes = {
763
766
  "prefect.input.name": context["parameter_name"],
764
767
  "prefect.input.type": type(result).__name__,
765
- },
768
+ }
769
+
770
+ trace.get_current_span().add_link(
771
+ context=trace.get_current_span(parameter_context).get_span_context(),
772
+ attributes=attributes,
766
773
  )
767
774
 
768
775
  return result
@@ -8,7 +8,7 @@ import threading
8
8
  from collections.abc import Iterable
9
9
  from contextlib import contextmanager
10
10
  from pathlib import Path, PureWindowsPath
11
- from typing import AnyStr, Optional, Union, cast
11
+ from typing import Any, AnyStr, Optional, Union, cast
12
12
 
13
13
  # fsspec has no stubs, see https://github.com/fsspec/filesystem_spec/issues/625
14
14
  import fsspec # type: ignore
@@ -114,7 +114,7 @@ def filename(path: str) -> str:
114
114
  return path.split(sep)[-1]
115
115
 
116
116
 
117
- def is_local_path(path: Union[str, pathlib.Path, OpenFile]) -> bool:
117
+ def is_local_path(path: Union[str, pathlib.Path, Any]) -> bool:
118
118
  """Check if the given path points to a local or remote file system"""
119
119
  if isinstance(path, str):
120
120
  try:
@@ -5,7 +5,7 @@ from pydantic_core import SchemaValidator, core_schema
5
5
 
6
6
  T = TypeVar("T", bound=BaseModel)
7
7
 
8
- ListValidator = SchemaValidator(
8
+ ListValidator: SchemaValidator = SchemaValidator(
9
9
  schema=core_schema.list_schema(
10
10
  items_schema=core_schema.dict_schema(
11
11
  keys_schema=core_schema.str_schema(), values_schema=core_schema.any_schema()
@@ -1,3 +1,4 @@
1
+ import warnings
1
2
  from typing import (
2
3
  Any,
3
4
  Callable,
@@ -10,18 +11,13 @@ from typing import (
10
11
  overload,
11
12
  )
12
13
 
13
- from jsonpatch import ( # type: ignore # no typing stubs available, see https://github.com/stefankoegl/python-json-patch/issues/158
14
- JsonPatch as JsonPatchBase,
15
- )
16
14
  from pydantic import (
17
15
  BaseModel,
18
- GetJsonSchemaHandler,
19
16
  Secret,
20
17
  TypeAdapter,
21
18
  ValidationError,
22
19
  )
23
- from pydantic.json_schema import JsonSchemaValue
24
- from pydantic_core import core_schema, to_jsonable_python
20
+ from pydantic_core import to_jsonable_python
25
21
  from typing_extensions import Literal
26
22
 
27
23
  from prefect.utilities.dispatch import get_dispatch_key, lookup_type, register_base_type
@@ -262,36 +258,6 @@ class PartialModel(Generic[M]):
262
258
  return f"PartialModel(cls={self.model_cls.__name__}, {dsp_fields})"
263
259
 
264
260
 
265
- class JsonPatch(JsonPatchBase):
266
- @classmethod
267
- def __get_pydantic_core_schema__(
268
- cls, source_type: Any, handler: GetJsonSchemaHandler
269
- ) -> core_schema.CoreSchema:
270
- return core_schema.typed_dict_schema(
271
- {"patch": core_schema.typed_dict_field(core_schema.dict_schema())}
272
- )
273
-
274
- @classmethod
275
- def __get_pydantic_json_schema__(
276
- cls, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
277
- ) -> JsonSchemaValue:
278
- json_schema = handler(core_schema)
279
- json_schema = handler.resolve_ref_schema(json_schema)
280
- json_schema.pop("required", None)
281
- json_schema.pop("properties", None)
282
- json_schema.update(
283
- {
284
- "type": "array",
285
- "format": "rfc6902",
286
- "items": {
287
- "type": "object",
288
- "additionalProperties": {"type": "string"},
289
- },
290
- }
291
- )
292
- return json_schema
293
-
294
-
295
261
  def custom_pydantic_encoder(
296
262
  type_encoders: dict[Any, Callable[[type[Any]], Any]], obj: Any
297
263
  ) -> Any:
@@ -382,3 +348,22 @@ def handle_secret_render(value: object, context: dict[str, Any]) -> object:
382
348
  elif isinstance(value, BaseModel):
383
349
  return value.model_dump(context=context)
384
350
  return value
351
+
352
+
353
+ def __getattr__(name: str) -> Any:
354
+ """
355
+ Handles imports from this module that are deprecated.
356
+ """
357
+
358
+ if name == "JsonPatch":
359
+ warnings.warn(
360
+ "JsonPatch is deprecated and will be removed after March 2025. "
361
+ "Please use `JsonPatch` from the `jsonpatch` package instead.",
362
+ DeprecationWarning,
363
+ stacklevel=2,
364
+ )
365
+ from ._deprecated import JsonPatch
366
+
367
+ return JsonPatch
368
+ else:
369
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")