prefect-client 2.16.8__py3-none-any.whl → 2.17.0__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 (89) hide show
  1. prefect/__init__.py +0 -18
  2. prefect/_internal/compatibility/deprecated.py +108 -5
  3. prefect/_internal/compatibility/experimental.py +9 -8
  4. prefect/_internal/concurrency/api.py +23 -42
  5. prefect/_internal/concurrency/waiters.py +25 -22
  6. prefect/_internal/pydantic/__init__.py +16 -3
  7. prefect/_internal/pydantic/_base_model.py +39 -4
  8. prefect/_internal/pydantic/_compat.py +69 -452
  9. prefect/_internal/pydantic/_flags.py +5 -0
  10. prefect/_internal/pydantic/_types.py +8 -0
  11. prefect/_internal/pydantic/utilities/__init__.py +0 -0
  12. prefect/_internal/pydantic/utilities/config_dict.py +72 -0
  13. prefect/_internal/pydantic/utilities/field_validator.py +135 -0
  14. prefect/_internal/pydantic/utilities/model_construct.py +56 -0
  15. prefect/_internal/pydantic/utilities/model_copy.py +55 -0
  16. prefect/_internal/pydantic/utilities/model_dump.py +136 -0
  17. prefect/_internal/pydantic/utilities/model_dump_json.py +112 -0
  18. prefect/_internal/pydantic/utilities/model_fields.py +50 -0
  19. prefect/_internal/pydantic/utilities/model_fields_set.py +29 -0
  20. prefect/_internal/pydantic/utilities/model_json_schema.py +82 -0
  21. prefect/_internal/pydantic/utilities/model_rebuild.py +80 -0
  22. prefect/_internal/pydantic/utilities/model_validate.py +75 -0
  23. prefect/_internal/pydantic/utilities/model_validate_json.py +68 -0
  24. prefect/_internal/pydantic/utilities/model_validator.py +79 -0
  25. prefect/_internal/pydantic/utilities/type_adapter.py +71 -0
  26. prefect/_internal/schemas/bases.py +1 -17
  27. prefect/_internal/schemas/validators.py +425 -4
  28. prefect/agent.py +1 -1
  29. prefect/blocks/kubernetes.py +7 -3
  30. prefect/blocks/notifications.py +18 -18
  31. prefect/blocks/webhook.py +1 -1
  32. prefect/client/base.py +7 -0
  33. prefect/client/cloud.py +1 -1
  34. prefect/client/orchestration.py +51 -11
  35. prefect/client/schemas/actions.py +367 -297
  36. prefect/client/schemas/filters.py +28 -28
  37. prefect/client/schemas/objects.py +78 -147
  38. prefect/client/schemas/responses.py +240 -60
  39. prefect/client/schemas/schedules.py +6 -8
  40. prefect/concurrency/events.py +2 -2
  41. prefect/context.py +4 -2
  42. prefect/deployments/base.py +6 -13
  43. prefect/deployments/deployments.py +34 -9
  44. prefect/deployments/runner.py +9 -27
  45. prefect/deprecated/packaging/base.py +5 -6
  46. prefect/deprecated/packaging/docker.py +19 -25
  47. prefect/deprecated/packaging/file.py +10 -5
  48. prefect/deprecated/packaging/orion.py +9 -4
  49. prefect/deprecated/packaging/serializers.py +8 -58
  50. prefect/engine.py +55 -618
  51. prefect/events/actions.py +16 -1
  52. prefect/events/clients.py +45 -13
  53. prefect/events/filters.py +19 -2
  54. prefect/events/related.py +4 -4
  55. prefect/events/schemas/automations.py +13 -2
  56. prefect/events/schemas/deployment_triggers.py +73 -5
  57. prefect/events/schemas/events.py +1 -1
  58. prefect/events/utilities.py +12 -4
  59. prefect/events/worker.py +26 -8
  60. prefect/exceptions.py +3 -8
  61. prefect/filesystems.py +7 -7
  62. prefect/flows.py +7 -3
  63. prefect/infrastructure/provisioners/ecs.py +1 -0
  64. prefect/logging/configuration.py +2 -2
  65. prefect/manifests.py +1 -8
  66. prefect/profiles.toml +1 -1
  67. prefect/pydantic/__init__.py +74 -2
  68. prefect/pydantic/main.py +26 -2
  69. prefect/serializers.py +6 -31
  70. prefect/settings.py +72 -26
  71. prefect/software/python.py +3 -5
  72. prefect/task_server.py +2 -2
  73. prefect/utilities/callables.py +1 -1
  74. prefect/utilities/collections.py +2 -1
  75. prefect/utilities/dispatch.py +1 -0
  76. prefect/utilities/engine.py +629 -0
  77. prefect/utilities/pydantic.py +1 -1
  78. prefect/utilities/schema_tools/validation.py +2 -2
  79. prefect/utilities/visualization.py +1 -1
  80. prefect/variables.py +88 -12
  81. prefect/workers/base.py +20 -11
  82. prefect/workers/block.py +4 -8
  83. prefect/workers/process.py +2 -5
  84. {prefect_client-2.16.8.dist-info → prefect_client-2.17.0.dist-info}/METADATA +4 -3
  85. {prefect_client-2.16.8.dist-info → prefect_client-2.17.0.dist-info}/RECORD +88 -72
  86. prefect/_internal/schemas/transformations.py +0 -106
  87. {prefect_client-2.16.8.dist-info → prefect_client-2.17.0.dist-info}/LICENSE +0 -0
  88. {prefect_client-2.16.8.dist-info → prefect_client-2.17.0.dist-info}/WHEEL +0 -0
  89. {prefect_client-2.16.8.dist-info → prefect_client-2.17.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,135 @@
1
+ """
2
+ Conditional decorator for fields depending on Pydantic version.
3
+ """
4
+
5
+ import functools
6
+ from inspect import signature
7
+ from typing import TYPE_CHECKING, Any, Callable, Dict, Literal, Optional, TypeVar, Union
8
+
9
+ from typing_extensions import TypeAlias
10
+
11
+ from prefect._internal.pydantic._flags import HAS_PYDANTIC_V2, USE_V2_MODELS
12
+
13
+ FieldValidatorModes: TypeAlias = Literal["before", "after", "wrap", "plain"]
14
+ T = TypeVar("T", bound=Callable[..., Any])
15
+
16
+ if TYPE_CHECKING:
17
+ from prefect._internal.pydantic._compat import BaseModel
18
+
19
+
20
+ def field_validator(
21
+ field: str,
22
+ /,
23
+ *fields: str,
24
+ mode: FieldValidatorModes = "after", # v2 only
25
+ check_fields: Union[bool, None] = None,
26
+ pre: bool = False, # v1 only
27
+ allow_reuse: Optional[bool] = None,
28
+ always: bool = False, # v1 only
29
+ ) -> Callable[[Any], Any]:
30
+ """Usage docs: https://docs.pydantic.dev/2.7/concepts/validators/#field-validators
31
+ Returns a decorator that conditionally applies Pydantic's `field_validator` or `validator`,
32
+ based on the Pydantic version available, for specified field(s) of a Pydantic model.
33
+
34
+ In Pydantic V2, it uses `field_validator` allowing more granular control over validation,
35
+ including pre-validation and post-validation modes. In Pydantic V1, it falls back to
36
+ using `validator`, which is less flexible but maintains backward compatibility.
37
+
38
+ Decorate methods on the class indicating that they should be used to validate fields.
39
+
40
+ Example usage:
41
+ ```py
42
+ from typing import Any
43
+
44
+ from pydantic import (
45
+ BaseModel,
46
+ ValidationError,
47
+ field_validator,
48
+ )
49
+
50
+ class Model(BaseModel):
51
+ a: str
52
+
53
+ @field_validator('a')
54
+ @classmethod
55
+ def ensure_foobar(cls, v: Any):
56
+ if 'foobar' not in v:
57
+ raise ValueError('"foobar" not found in a')
58
+ return v
59
+
60
+ print(repr(Model(a='this is foobar good')))
61
+ #> Model(a='this is foobar good')
62
+
63
+ try:
64
+ Model(a='snap')
65
+ except ValidationError as exc_info:
66
+ print(exc_info)
67
+ '''
68
+ 1 validation error for Model
69
+ a
70
+ Value error, "foobar" not found in a [type=value_error, input_value='snap', input_type=str]
71
+ '''
72
+ ```
73
+
74
+ For more in depth examples, see https://docs.pydantic.dev/latest/concepts/validators/#field-validators
75
+
76
+ Args:
77
+ field: The first field the `field_validator` should be called on; this is separate
78
+ from `fields` to ensure an error is raised if you don't pass at least one.
79
+ *fields: Additional field(s) the `field_validator` should be called on.
80
+ mode: Specifies whether to validate the fields before or after validation.
81
+ check_fields: Whether to check that the fields actually exist on the model.
82
+
83
+ Returns:
84
+ A decorator that can be used to decorate a function to be used as a field_validator.
85
+
86
+ Raises:
87
+ PydanticUserError:
88
+ - If `@field_validator` is used bare (with no fields).
89
+ - If the args passed to `@field_validator` as fields are not strings.
90
+ - If `@field_validator` applied to instance methods.
91
+ """
92
+
93
+ def decorator(validate_func: T) -> T:
94
+ if USE_V2_MODELS:
95
+ from pydantic import field_validator # type: ignore
96
+
97
+ return field_validator(
98
+ field, *fields, mode=mode, check_fields=check_fields
99
+ )(validate_func)
100
+ elif HAS_PYDANTIC_V2:
101
+ from pydantic.v1 import validator # type: ignore
102
+ else:
103
+ from pydantic import validator
104
+
105
+ # Extract the parameters of the validate_func function
106
+ # e.g. if validate_func has a signature of (cls, v, values, config), we want to
107
+ # filter the kwargs to include only those expected by validate_func, which may
108
+ # look like (cls, v) or (cls, v, values) etc.
109
+ validate_func_params = signature(validate_func).parameters
110
+
111
+ @functools.wraps(validate_func)
112
+ def wrapper(
113
+ cls: "BaseModel",
114
+ v: Any,
115
+ **kwargs: Any,
116
+ ) -> Any:
117
+ filtered_kwargs: Dict[str, Any] = {
118
+ k: v for k, v in kwargs.items() if k in validate_func_params
119
+ }
120
+
121
+ return validate_func(cls, v, **filtered_kwargs)
122
+
123
+ # In Pydantic V1, `allow_reuse` is by default False, while in Pydantic V2, it is by default True.
124
+ # We default to False in Pydantic V1 to maintain backward compatibility
125
+ # e.g. One uses @validator("a", pre=True, allow_reuse=True) in Pydantic V1
126
+ validator_kwargs: Dict[str, Any] = {
127
+ "pre": pre,
128
+ "always": always,
129
+ "check_fields": check_fields if check_fields is not None else True,
130
+ "allow_reuse": allow_reuse if allow_reuse is not None else False,
131
+ }
132
+
133
+ return validator(field, *fields, **validator_kwargs)(wrapper) # type: ignore
134
+
135
+ return decorator
@@ -0,0 +1,56 @@
1
+ import typing
2
+
3
+ from typing_extensions import Self
4
+
5
+ from prefect._internal.pydantic._base_model import BaseModel
6
+ from prefect._internal.pydantic._flags import USE_V2_MODELS
7
+
8
+ T = typing.TypeVar("T", bound="BaseModel")
9
+
10
+
11
+ def model_construct(
12
+ model: typing.Type[T],
13
+ _fields_set: typing.Optional[typing.Set[str]] = None,
14
+ **values: typing.Any,
15
+ ) -> T:
16
+ """Creates a new instance of the `model` class with validated data.
17
+
18
+ Creates a new model setting `__dict__` and `__pydantic_fields_set__` from trusted or pre-validated data.
19
+ Default values are respected, but no other validation is performed.
20
+
21
+ Args:
22
+ _fields_set: The set of field names accepted for the Model instance.
23
+ values: Trusted or pre-validated data dictionary.
24
+
25
+ Returns:
26
+ A new instance of the `model` class with validated data.
27
+ """
28
+ if USE_V2_MODELS:
29
+ return model.model_construct(_fields_set=_fields_set, **values)
30
+ else:
31
+ return getattr(model, "construct")(**values)
32
+
33
+
34
+ class ModelConstructMixin(BaseModel):
35
+ @classmethod
36
+ def model_construct(
37
+ cls: typing.Type["Self"],
38
+ _fields_set: typing.Optional[typing.Set[str]] = None,
39
+ **values: typing.Any,
40
+ ) -> "Self":
41
+ """Creates a new instance of the `model` class with validated data.
42
+
43
+ Creates a new model setting `__dict__` and `__pydantic_fields_set__` from trusted or pre-validated data.
44
+ Default values are respected, but no other validation is performed.
45
+
46
+ Args:
47
+ _fields_set: The set of field names accepted for the Model instance.
48
+ values: Trusted or pre-validated data dictionary.
49
+
50
+ Returns:
51
+ A new instance of the `model` class with validated data.
52
+ """
53
+ return model_construct(cls, _fields_set=_fields_set, **values)
54
+
55
+
56
+ __all__ = ["model_construct", "ModelConstructMixin"]
@@ -0,0 +1,55 @@
1
+ import typing
2
+
3
+ from typing_extensions import Self
4
+
5
+ from prefect._internal.pydantic._base_model import BaseModel
6
+ from prefect._internal.pydantic._flags import USE_V2_MODELS
7
+
8
+ T = typing.TypeVar("T", bound="BaseModel")
9
+
10
+
11
+ def model_copy( # type: ignore[no-redef]
12
+ model_instance: T,
13
+ *,
14
+ update: typing.Optional[typing.Dict[str, typing.Any]] = None,
15
+ deep: bool = False,
16
+ ) -> T:
17
+ """
18
+ Returns a copy of the model.
19
+
20
+ Args:
21
+ update: Values to change/add in the new model. Note: the data is not validated
22
+ before creating the new model. You should trust this data.
23
+ deep: Set to `True` to make a deep copy of the model.
24
+
25
+ Returns:
26
+ New model instance.
27
+ """
28
+ if USE_V2_MODELS:
29
+ return model_instance.model_copy(update=update, deep=deep)
30
+ else:
31
+ return getattr(model_instance, "copy")(update=update, deep=deep)
32
+
33
+
34
+ class ModelCopyMixin(BaseModel):
35
+ def model_copy(
36
+ self: "Self",
37
+ *,
38
+ update: typing.Optional[typing.Dict[str, typing.Any]] = None,
39
+ deep: bool = False,
40
+ ) -> "Self":
41
+ """
42
+ Returns a copy of the model.
43
+
44
+ Args:
45
+ update: Values to change/add in the new model. Note: the data is not validated
46
+ before creating the new model. You should trust this data.
47
+ deep: Set to `True` to make a deep copy of the model.
48
+
49
+ Returns:
50
+ New model instance.
51
+ """
52
+ return model_copy(self, update=update, deep=deep)
53
+
54
+
55
+ __all__ = ["model_copy"]
@@ -0,0 +1,136 @@
1
+ import typing
2
+ import warnings as python_warnings
3
+
4
+ from pydantic_core import from_json
5
+ from typing_extensions import Self
6
+
7
+ from prefect._internal.pydantic._base_model import BaseModel
8
+ from prefect._internal.pydantic._flags import USE_V2_MODELS
9
+
10
+ if typing.TYPE_CHECKING:
11
+ from prefect._internal.pydantic._types import IncEx
12
+
13
+ T = typing.TypeVar("T", bound="BaseModel")
14
+
15
+
16
+ def model_dump( # type: ignore[no-redef]
17
+ model_instance: "BaseModel",
18
+ *,
19
+ mode: typing.Union[typing.Literal["json", "python"], str] = "python",
20
+ include: "IncEx" = None,
21
+ exclude: "IncEx" = None,
22
+ by_alias: bool = False,
23
+ exclude_unset: bool = False,
24
+ exclude_defaults: bool = False,
25
+ exclude_none: bool = False,
26
+ round_trip: bool = False,
27
+ warnings: bool = True,
28
+ ) -> typing.Dict[str, typing.Any]:
29
+ """
30
+ Generate a dictionary representation of the model, optionally specifying which fields to include or exclude.
31
+
32
+ Args:
33
+ mode: The mode in which `to_python` should run.
34
+ If mode is 'json', the output will only contain JSON serializable types.
35
+ If mode is 'python', the output may contain non-JSON-serializable Python objects.
36
+ include: A set of fields to include in the output.
37
+ exclude: A set of fields to exclude from the output.
38
+ context: Additional context to pass to the serializer.
39
+ by_alias: Whether to use the field's alias in the dictionary key if defined.
40
+ exclude_unset: Whether to exclude fields that have not been explicitly set.
41
+ exclude_defaults: Whether to exclude fields that are set to their default value.
42
+ exclude_none: Whether to exclude fields that have a value of `None`.
43
+ round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].
44
+ warnings: Whether to log warnings when invalid fields are encountered.
45
+ serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
46
+
47
+ Returns:
48
+ A dictionary representation of the model.
49
+ """
50
+ if USE_V2_MODELS:
51
+ return model_instance.model_dump(
52
+ mode=mode,
53
+ include=include,
54
+ exclude=exclude,
55
+ by_alias=by_alias,
56
+ exclude_unset=exclude_unset,
57
+ exclude_defaults=exclude_defaults,
58
+ exclude_none=exclude_none,
59
+ round_trip=round_trip,
60
+ warnings=warnings,
61
+ )
62
+ else:
63
+ if mode == "json":
64
+ with python_warnings.catch_warnings():
65
+ python_warnings.simplefilter("ignore")
66
+ return from_json(
67
+ model_instance.json(
68
+ include=include,
69
+ exclude=exclude,
70
+ by_alias=by_alias,
71
+ exclude_unset=exclude_unset,
72
+ exclude_defaults=exclude_defaults,
73
+ exclude_none=exclude_none,
74
+ )
75
+ )
76
+
77
+ return getattr(model_instance, "dict")(
78
+ include=include,
79
+ exclude=exclude,
80
+ by_alias=by_alias,
81
+ exclude_unset=exclude_unset,
82
+ exclude_defaults=exclude_defaults,
83
+ exclude_none=exclude_none,
84
+ )
85
+
86
+
87
+ class ModelDumpMixin(BaseModel):
88
+ def model_dump(
89
+ self: "Self",
90
+ *,
91
+ mode: typing.Union[typing.Literal["json", "python"], str] = "python",
92
+ include: "IncEx" = None,
93
+ exclude: "IncEx" = None,
94
+ by_alias: bool = False,
95
+ exclude_unset: bool = False,
96
+ exclude_defaults: bool = False,
97
+ exclude_none: bool = False,
98
+ round_trip: bool = False,
99
+ warnings: bool = True,
100
+ ) -> typing.Dict[str, typing.Any]:
101
+ """
102
+ Generate a dictionary representation of the model, optionally specifying which fields to include or exclude.
103
+
104
+ Args:
105
+ mode: The mode in which `to_python` should run.
106
+ If mode is 'json', the output will only contain JSON serializable types.
107
+ If mode is 'python', the output may contain non-JSON-serializable Python objects.
108
+ include: A set of fields to include in the output.
109
+ exclude: A set of fields to exclude from the output.
110
+ context: Additional context to pass to the serializer.
111
+ by_alias: Whether to use the field's alias in the dictionary key if defined.
112
+ exclude_unset: Whether to exclude fields that have not been explicitly set.
113
+ exclude_defaults: Whether to exclude fields that are set to their default value.
114
+ exclude_none: Whether to exclude fields that have a value of `None`.
115
+ round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].
116
+ warnings: Whether to log warnings when invalid fields are encountered.
117
+ serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
118
+
119
+ Returns:
120
+ A dictionary representation of the model.
121
+ """
122
+ return model_dump(
123
+ self,
124
+ mode=mode,
125
+ include=include,
126
+ exclude=exclude,
127
+ by_alias=by_alias,
128
+ exclude_unset=exclude_unset,
129
+ exclude_defaults=exclude_defaults,
130
+ exclude_none=exclude_none,
131
+ round_trip=round_trip,
132
+ warnings=warnings,
133
+ )
134
+
135
+
136
+ __all__ = ["model_dump", "ModelDumpMixin"]
@@ -0,0 +1,112 @@
1
+ import typing
2
+
3
+ from typing_extensions import Self
4
+
5
+ from prefect._internal.pydantic._base_model import BaseModel
6
+ from prefect._internal.pydantic._flags import USE_V2_MODELS
7
+
8
+ if typing.TYPE_CHECKING:
9
+ from prefect._internal.pydantic._types import IncEx
10
+
11
+ T = typing.TypeVar("T", bound="BaseModel")
12
+
13
+
14
+ def model_dump_json(
15
+ model_instance: "BaseModel",
16
+ *,
17
+ indent: typing.Optional[int] = None,
18
+ include: "IncEx" = None,
19
+ exclude: "IncEx" = None,
20
+ by_alias: bool = False,
21
+ exclude_unset: bool = False,
22
+ exclude_defaults: bool = False,
23
+ exclude_none: bool = False,
24
+ round_trip: bool = False,
25
+ warnings: bool = True,
26
+ ) -> str:
27
+ """
28
+ Generate a JSON representation of the model, optionally specifying which fields to include or exclude.
29
+
30
+ Args:
31
+ indent: If provided, the number of spaces to indent the JSON output.
32
+ include: A list of fields to include in the output.
33
+ exclude: A list of fields to exclude from the output.
34
+ by_alias: Whether to use the field's alias in the dictionary key if defined.
35
+ exclude_unset: Whether to exclude fields that have not been explicitly set.
36
+ exclude_defaults: Whether to exclude fields that are set to their default value.
37
+ exclude_none: Whether to exclude fields that have a value of `None`.
38
+ round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].
39
+ warnings: Whether to log warnings when invalid fields are encountered.
40
+
41
+ Returns:
42
+ A JSON representation of the model.
43
+ """
44
+ if USE_V2_MODELS:
45
+ return model_instance.model_dump_json(
46
+ indent=indent,
47
+ include=include,
48
+ exclude=exclude,
49
+ by_alias=by_alias,
50
+ exclude_unset=exclude_unset,
51
+ exclude_defaults=exclude_defaults,
52
+ exclude_none=exclude_none,
53
+ round_trip=round_trip,
54
+ warnings=warnings,
55
+ )
56
+ else:
57
+ return getattr(model_instance, "json")(
58
+ include=include,
59
+ exclude=exclude,
60
+ by_alias=by_alias,
61
+ exclude_unset=exclude_unset,
62
+ exclude_defaults=exclude_defaults,
63
+ exclude_none=exclude_none,
64
+ )
65
+
66
+
67
+ class ModelDumpJsonMixin(BaseModel):
68
+ def model_dump_json(
69
+ self: "Self",
70
+ *,
71
+ indent: typing.Optional[int] = None,
72
+ include: "IncEx" = None,
73
+ exclude: "IncEx" = None,
74
+ by_alias: bool = False,
75
+ exclude_unset: bool = False,
76
+ exclude_defaults: bool = False,
77
+ exclude_none: bool = False,
78
+ round_trip: bool = False,
79
+ warnings: bool = True,
80
+ ) -> str:
81
+ """
82
+ Generate a JSON representation of the model, optionally specifying which fields to include or exclude.
83
+
84
+ Args:
85
+ indent: If provided, the number of spaces to indent the JSON output.
86
+ include: A list of fields to include in the output.
87
+ exclude: A list of fields to exclude from the output.
88
+ by_alias: Whether to use the field's alias in the dictionary key if defined.
89
+ exclude_unset: Whether to exclude fields that have not been explicitly set.
90
+ exclude_defaults: Whether to exclude fields that are set to their default value.
91
+ exclude_none: Whether to exclude fields that have a value of `None`.
92
+ round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].
93
+ warnings: Whether to log warnings when invalid fields are encountered.
94
+
95
+ Returns:
96
+ A JSON representation of the model.
97
+ """
98
+ return model_dump_json(
99
+ self,
100
+ indent=indent,
101
+ include=include,
102
+ exclude=exclude,
103
+ by_alias=by_alias,
104
+ exclude_unset=exclude_unset,
105
+ exclude_defaults=exclude_defaults,
106
+ exclude_none=exclude_none,
107
+ round_trip=round_trip,
108
+ warnings=warnings,
109
+ )
110
+
111
+
112
+ __all__ = ["model_dump_json", "ModelDumpJsonMixin"]
@@ -0,0 +1,50 @@
1
+ import operator
2
+ import typing
3
+
4
+ from prefect._internal.pydantic._base_model import BaseModel, FieldInfo
5
+ from prefect._internal.pydantic._flags import HAS_PYDANTIC_V2, USE_PYDANTIC_V2
6
+
7
+ T = typing.TypeVar("T")
8
+
9
+ if HAS_PYDANTIC_V2 and USE_PYDANTIC_V2:
10
+
11
+ class ModelFieldMixin(BaseModel): # type: ignore [no-redef]
12
+ pass
13
+
14
+ else:
15
+
16
+ def cast_model_field_to_field_info(model_field: typing.Any) -> FieldInfo:
17
+ class _FieldInfo(BaseModel):
18
+ model_field: typing.Any
19
+
20
+ @property
21
+ def annotation(self) -> typing.Any:
22
+ return getattr(self.model_field, "outer_type_")
23
+
24
+ @property
25
+ def frozen(self) -> bool:
26
+ return not operator.attrgetter("field_info.allow_mutation")(
27
+ self.model_field
28
+ )
29
+
30
+ @property
31
+ def json_schema_extra(self) -> typing.Dict[str, typing.Any]:
32
+ return operator.attrgetter("field_info.extra")(self.model_field)
33
+
34
+ def __getattr__(self, key: str) -> typing.Any:
35
+ return getattr(self.model_field, key)
36
+
37
+ def __repr__(self) -> str:
38
+ return repr(self.model_field)
39
+
40
+ return typing.cast(FieldInfo, _FieldInfo(model_field=model_field))
41
+
42
+ class ModelFieldMixin(BaseModel):
43
+ model_fields: typing.ClassVar[typing.Dict[str, FieldInfo]]
44
+
45
+ def __init_subclass__(cls, **kwargs: typing.Any) -> None:
46
+ cls.model_fields = {
47
+ field_name: cast_model_field_to_field_info(field)
48
+ for field_name, field in getattr(cls, "__fields__", {}).items()
49
+ }
50
+ super().__init_subclass__(**kwargs)
@@ -0,0 +1,29 @@
1
+ import typing
2
+
3
+ from prefect._internal.pydantic._base_model import BaseModel
4
+ from prefect._internal.pydantic._flags import USE_V2_MODELS
5
+
6
+
7
+ def model_fields_set(model_instance: "BaseModel") -> typing.Set[str]:
8
+ """
9
+ Returns a set of the model's fields.
10
+ """
11
+ if USE_V2_MODELS:
12
+ return getattr(model_instance, "__pydantic_fields_set__")
13
+ else:
14
+ return getattr(model_instance, "__fields_set__")
15
+
16
+
17
+ class ModelFieldsSetMixin(BaseModel):
18
+ @property
19
+ def model_fields_set(self) -> typing.Set[str]:
20
+ """Returns the set of fields that have been explicitly set on this model instance.
21
+
22
+ Returns:
23
+ A set of strings representing the fields that have been set,
24
+ i.e. that were not filled from defaults.
25
+ """
26
+ return model_fields_set(self)
27
+
28
+
29
+ __all__ = ["ModelFieldsSetMixin", "model_fields_set"]
@@ -0,0 +1,82 @@
1
+ import typing
2
+
3
+ from typing_extensions import Self
4
+
5
+ from prefect._internal.pydantic._base_model import BaseModel
6
+ from prefect._internal.pydantic._flags import USE_V2_MODELS
7
+
8
+ if typing.TYPE_CHECKING:
9
+ from prefect._internal.pydantic._types import JsonSchemaMode
10
+
11
+ from prefect._internal.pydantic._types import DEFAULT_REF_TEMPLATE
12
+
13
+ T = typing.TypeVar("T", bound="BaseModel")
14
+
15
+
16
+ def model_json_schema(
17
+ model: typing.Type[T],
18
+ by_alias: bool = True,
19
+ ref_template: str = DEFAULT_REF_TEMPLATE,
20
+ schema_generator: typing.Any = None,
21
+ mode: "JsonSchemaMode" = "validation",
22
+ ) -> typing.Dict[str, typing.Any]:
23
+ """
24
+ Generates a JSON schema for a model class.
25
+
26
+ Args:
27
+ by_alias: Whether to use attribute aliases or not.
28
+ ref_template: The reference template.
29
+ schema_generator: To override the logic used to generate the JSON schema, as a subclass of
30
+ `GenerateJsonSchema` with your desired modifications
31
+ mode: The mode in which to generate the schema.
32
+
33
+ Returns:
34
+ The JSON schema for the given model class.
35
+ """
36
+ if USE_V2_MODELS:
37
+ return model.model_json_schema(
38
+ by_alias=by_alias,
39
+ ref_template=ref_template,
40
+ mode=mode,
41
+ # We've changed the type of schema_generator of 'schema_generator' to 'typing.Any',
42
+ # which is will throw an error if its None. So, we've to pass it only if its not None.
43
+ **{"schema_generator": schema_generator} if schema_generator else {},
44
+ )
45
+ else:
46
+ return getattr(model, "schema")(
47
+ by_alias=by_alias,
48
+ ref_template=ref_template,
49
+ )
50
+
51
+
52
+ class ModelJsonSchemaMixin(BaseModel):
53
+ @classmethod
54
+ def model_json_schema(
55
+ cls: typing.Type["Self"],
56
+ by_alias: bool = True,
57
+ ref_template: str = DEFAULT_REF_TEMPLATE,
58
+ schema_generator: typing.Any = None,
59
+ mode: "JsonSchemaMode" = "validation",
60
+ ) -> typing.Dict[str, typing.Any]:
61
+ """
62
+ Generates a JSON schema for a model class.
63
+
64
+ Args:
65
+ by_alias: Whether to use attribute aliases or not.
66
+ ref_template: The reference template.
67
+ schema_generator: To override the logic used to generate the JSON schema, as a subclass of
68
+ `GenerateJsonSchema` with your desired modifications
69
+ mode: The mode in which to generate the schema.
70
+
71
+ Returns:
72
+ The JSON schema for the given model class.
73
+ """
74
+ return model_json_schema(
75
+ cls,
76
+ by_alias=by_alias,
77
+ ref_template=ref_template,
78
+ mode=mode,
79
+ )
80
+
81
+
82
+ __all__ = ["model_json_schema", "ModelJsonSchemaMixin"]