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
@@ -0,0 +1,220 @@
1
+ from contextlib import contextmanager
2
+ from contextvars import ContextVar, Token
3
+ from typing import (
4
+ Any,
5
+ Callable,
6
+ Generator,
7
+ List,
8
+ Optional,
9
+ Type,
10
+ TypeVar,
11
+ )
12
+
13
+ from pydantic import Field
14
+
15
+ from prefect.context import ContextModel
16
+ from prefect.records import RecordStore
17
+ from prefect.utilities.collections import AutoEnum
18
+
19
+ T = TypeVar("T")
20
+
21
+
22
+ class IsolationLevel(AutoEnum):
23
+ READ_COMMITTED = AutoEnum.auto()
24
+ SERIALIZABLE = AutoEnum.auto()
25
+
26
+
27
+ class CommitMode(AutoEnum):
28
+ EAGER = AutoEnum.auto()
29
+ LAZY = AutoEnum.auto()
30
+ OFF = AutoEnum.auto()
31
+
32
+
33
+ class TransactionState(AutoEnum):
34
+ PENDING = AutoEnum.auto()
35
+ ACTIVE = AutoEnum.auto()
36
+ STAGED = AutoEnum.auto()
37
+ COMMITTED = AutoEnum.auto()
38
+ ROLLED_BACK = AutoEnum.auto()
39
+
40
+
41
+ class Transaction(ContextModel):
42
+ """
43
+ A base model for transaction state.
44
+ """
45
+
46
+ store: Optional[RecordStore] = None
47
+ key: Optional[str] = None
48
+ children: List["Transaction"] = Field(default_factory=list)
49
+ commit_mode: Optional[CommitMode] = None
50
+ state: TransactionState = TransactionState.PENDING
51
+ on_commit_hooks: List[Callable[["Transaction"], None]] = Field(default_factory=list)
52
+ on_rollback_hooks: List[Callable[["Transaction"], None]] = Field(
53
+ default_factory=list
54
+ )
55
+ _staged_value: Any = None
56
+ __var__ = ContextVar("transaction")
57
+
58
+ def is_committed(self) -> bool:
59
+ return self.state == TransactionState.COMMITTED
60
+
61
+ def is_rolled_back(self) -> bool:
62
+ return self.state == TransactionState.ROLLED_BACK
63
+
64
+ def is_staged(self) -> bool:
65
+ return self.state == TransactionState.STAGED
66
+
67
+ def is_pending(self) -> bool:
68
+ return self.state == TransactionState.PENDING
69
+
70
+ def is_active(self) -> bool:
71
+ return self.state == TransactionState.ACTIVE
72
+
73
+ def __enter__(self):
74
+ if self._token is not None:
75
+ raise RuntimeError(
76
+ "Context already entered. Context enter calls cannot be nested."
77
+ )
78
+ # set default commit behavior
79
+ if self.commit_mode is None:
80
+ parent = get_transaction()
81
+
82
+ # either inherit from parent or set a default of eager
83
+ if parent:
84
+ self.commit_mode = parent.commit_mode
85
+ else:
86
+ self.commit_mode = CommitMode.EAGER
87
+
88
+ # this needs to go before begin, which could set the state to committed
89
+ self.state = TransactionState.ACTIVE
90
+ self.begin()
91
+ self._token = self.__var__.set(self)
92
+ return self
93
+
94
+ def __exit__(self, exc_type, exc_val, exc_tb):
95
+ if not self._token:
96
+ raise RuntimeError(
97
+ "Asymmetric use of context. Context exit called without an enter."
98
+ )
99
+ if exc_type:
100
+ self.rollback()
101
+ self.reset()
102
+ raise exc_val
103
+
104
+ if self.commit_mode == CommitMode.EAGER:
105
+ self.commit()
106
+
107
+ # if parent, let them take responsibility
108
+ if self.get_parent():
109
+ self.reset()
110
+ return
111
+
112
+ if self.commit_mode == CommitMode.OFF:
113
+ # if no one took responsibility to commit, rolling back
114
+ # note that rollback returns if already committed
115
+ self.rollback()
116
+ elif self.commit_mode == CommitMode.LAZY:
117
+ # no one left to take responsibility for committing
118
+ self.commit()
119
+
120
+ self.reset()
121
+
122
+ def begin(self):
123
+ # currently we only support READ_COMMITTED isolation
124
+ # i.e., no locking behavior
125
+ if self.store and self.store.exists(key=self.key):
126
+ self.state = TransactionState.COMMITTED
127
+
128
+ def read(self) -> dict:
129
+ return self.store.read(key=self.key)
130
+
131
+ def reset(self) -> None:
132
+ parent = self.get_parent()
133
+
134
+ if parent:
135
+ # parent takes responsibility
136
+ parent.add_child(self)
137
+
138
+ self.__var__.reset(self._token)
139
+ self._token = None
140
+
141
+ # do this below reset so that get_transaction() returns the relevant txn
142
+ if parent and self.state == TransactionState.ROLLED_BACK:
143
+ parent.rollback()
144
+
145
+ def add_child(self, transaction: "Transaction") -> None:
146
+ self.children.append(transaction)
147
+
148
+ def get_parent(self) -> Optional["Transaction"]:
149
+ prev_var = getattr(self._token, "old_value")
150
+ if prev_var != Token.MISSING:
151
+ parent = prev_var
152
+ else:
153
+ parent = None
154
+ return parent
155
+
156
+ def commit(self) -> bool:
157
+ if self.state in [TransactionState.ROLLED_BACK, TransactionState.COMMITTED]:
158
+ return False
159
+
160
+ try:
161
+ for child in self.children:
162
+ child.commit()
163
+
164
+ for hook in self.on_commit_hooks:
165
+ hook(self)
166
+
167
+ if self.store:
168
+ self.store.write(key=self.key, value=self._staged_value)
169
+ self.state = TransactionState.COMMITTED
170
+ return True
171
+ except Exception:
172
+ self.rollback()
173
+ return False
174
+
175
+ def stage(
176
+ self, value: dict, on_rollback_hooks: list, on_commit_hooks: list
177
+ ) -> None:
178
+ """
179
+ Stage a value to be committed later.
180
+ """
181
+ if self.state != TransactionState.COMMITTED:
182
+ self._staged_value = value
183
+ self.on_rollback_hooks += on_rollback_hooks
184
+ self.on_commit_hooks += on_commit_hooks
185
+ self.state = TransactionState.STAGED
186
+
187
+ def rollback(self) -> bool:
188
+ if self.state in [TransactionState.ROLLED_BACK, TransactionState.COMMITTED]:
189
+ return False
190
+
191
+ try:
192
+ for hook in reversed(self.on_rollback_hooks):
193
+ hook(self)
194
+
195
+ self.state = TransactionState.ROLLED_BACK
196
+
197
+ for child in reversed(self.children):
198
+ child.rollback()
199
+
200
+ return True
201
+ except Exception:
202
+ return False
203
+
204
+ @classmethod
205
+ def get_active(cls: Type[T]) -> Optional[T]:
206
+ return cls.__var__.get(None)
207
+
208
+
209
+ def get_transaction() -> Transaction:
210
+ return Transaction.get_active()
211
+
212
+
213
+ @contextmanager
214
+ def transaction(
215
+ key: Optional[str] = None,
216
+ store: Optional[RecordStore] = None,
217
+ commit_mode: CommitMode = CommitMode.LAZY,
218
+ ) -> Generator[Transaction, None, None]:
219
+ with Transaction(key=key, store=store, commit_mode=commit_mode) as txn:
220
+ yield txn
prefect/types/__init__.py CHANGED
@@ -1,112 +1,91 @@
1
- from typing import Any, Callable, ClassVar, Generator
1
+ from typing import Annotated, Any, Dict, List, Union
2
+ import orjson
3
+ import pydantic
2
4
 
3
- from pydantic_core import core_schema, CoreSchema, SchemaValidator
4
- from typing_extensions import Self
5
- from datetime import timedelta
5
+ from pydantic import (
6
+ BeforeValidator,
7
+ Field,
8
+ StrictBool,
9
+ StrictFloat,
10
+ StrictInt,
11
+ StrictStr,
12
+ )
13
+ from zoneinfo import available_timezones
6
14
 
15
+ MAX_VARIABLE_NAME_LENGTH = 255
16
+ MAX_VARIABLE_VALUE_LENGTH = 5000
7
17
 
8
- class NonNegativeInteger(int):
9
- """An integer that must be greater than or equal to 0."""
18
+ timezone_set = available_timezones()
10
19
 
11
- schema: ClassVar[CoreSchema] = core_schema.int_schema(ge=0)
20
+ NonNegativeInteger = Annotated[int, Field(ge=0)]
21
+ PositiveInteger = Annotated[int, Field(gt=0)]
22
+ NonNegativeFloat = Annotated[float, Field(ge=0.0)]
23
+ TimeZone = Annotated[str, Field(default="UTC", pattern="|".join(timezone_set))]
12
24
 
13
- @classmethod
14
- def __get_validators__(cls) -> Generator[Callable[..., Any], None, None]:
15
- yield cls.validate
16
25
 
17
- @classmethod
18
- def __get_pydantic_core_schema__(
19
- cls, source_type: Any, handler: Callable[..., Any]
20
- ) -> CoreSchema:
21
- return cls.schema
26
+ BANNED_CHARACTERS = ["/", "%", "&", ">", "<"]
22
27
 
23
- @classmethod
24
- def validate(cls, v: Any) -> Self:
25
- return SchemaValidator(schema=cls.schema).validate_python(v)
28
+ WITHOUT_BANNED_CHARACTERS = r"^[^" + "".join(BANNED_CHARACTERS) + "]+$"
29
+ Name = Annotated[str, Field(pattern=WITHOUT_BANNED_CHARACTERS)]
26
30
 
31
+ WITHOUT_BANNED_CHARACTERS_EMPTY_OK = r"^[^" + "".join(BANNED_CHARACTERS) + "]*$"
32
+ NameOrEmpty = Annotated[str, Field(pattern=WITHOUT_BANNED_CHARACTERS_EMPTY_OK)]
27
33
 
28
- class PositiveInteger(int):
29
- """An integer that must be greater than 0."""
30
34
 
31
- schema: ClassVar[CoreSchema] = core_schema.int_schema(gt=0)
35
+ def non_emptyish(value: str) -> str:
36
+ if not value.strip("' \""):
37
+ raise ValueError("name cannot be an empty string")
32
38
 
33
- @classmethod
34
- def __get_validators__(cls) -> Generator[Callable[..., Any], None, None]:
35
- yield cls.validate
39
+ return value
36
40
 
37
- @classmethod
38
- def __get_pydantic_core_schema__(
39
- cls, source_type: Any, handler: Callable[..., Any]
40
- ) -> CoreSchema:
41
- return cls.schema
42
41
 
43
- @classmethod
44
- def validate(cls, v: Any) -> Self:
45
- return SchemaValidator(schema=cls.schema).validate_python(v)
46
-
47
-
48
- class NonNegativeFloat(float):
49
- schema: ClassVar[CoreSchema] = core_schema.float_schema(ge=0)
50
-
51
- @classmethod
52
- def __get_validators__(cls) -> Generator[Callable[..., Any], None, None]:
53
- yield cls.validate
54
-
55
- @classmethod
56
- def __get_pydantic_core_schema__(
57
- cls, source_type: Any, handler: Callable[..., Any]
58
- ) -> CoreSchema:
59
- return cls.schema
60
-
61
- @classmethod
62
- def validate(cls, v: Any) -> Self:
63
- return SchemaValidator(schema=cls.schema).validate_python(v)
64
-
65
-
66
- class NonNegativeDuration(timedelta):
67
- """A timedelta that must be greater than or equal to 0."""
42
+ NonEmptyishName = Annotated[
43
+ str,
44
+ Field(pattern=WITHOUT_BANNED_CHARACTERS),
45
+ BeforeValidator(non_emptyish),
46
+ ]
68
47
 
69
- schema: ClassVar = core_schema.timedelta_schema(ge=timedelta(seconds=0))
70
48
 
71
- @classmethod
72
- def __get_validators__(cls) -> Generator[Callable[..., Any], None, None]:
73
- yield cls.validate
49
+ VariableValue = Union[
50
+ StrictStr,
51
+ StrictInt,
52
+ StrictBool,
53
+ StrictFloat,
54
+ None,
55
+ Dict[str, Any],
56
+ List[Any],
57
+ ]
74
58
 
75
- @classmethod
76
- def __get_pydantic_core_schema__(
77
- cls, source_type: Any, handler: Callable[..., Any]
78
- ) -> CoreSchema:
79
- return cls.schema
80
59
 
81
- @classmethod
82
- def validate(cls, v: Any) -> Self:
83
- return SchemaValidator(schema=cls.schema).validate_python(v)
60
+ def check_variable_value(value: object) -> object:
61
+ try:
62
+ json_string = orjson.dumps(value)
63
+ except orjson.JSONEncodeError:
64
+ raise ValueError("Variable value must be serializable to JSON")
84
65
 
66
+ if value is not None and len(json_string) > MAX_VARIABLE_VALUE_LENGTH:
67
+ raise ValueError(
68
+ f"Variable value must be less than {MAX_VARIABLE_VALUE_LENGTH} characters"
69
+ )
70
+ return value
85
71
 
86
- class PositiveDuration(timedelta):
87
- """A timedelta that must be greater than 0."""
88
72
 
89
- schema: ClassVar = core_schema.timedelta_schema(gt=timedelta(seconds=0))
73
+ StrictVariableValue = Annotated[VariableValue, BeforeValidator(check_variable_value)]
90
74
 
91
- @classmethod
92
- def __get_validators__(cls) -> Generator[Callable[..., Any], None, None]:
93
- yield cls.validate
75
+ LaxUrl = Annotated[str, BeforeValidator(lambda x: str(x).strip())]
94
76
 
95
- @classmethod
96
- def __get_pydantic_core_schema__(
97
- cls, source_type: Any, handler: Callable[..., Any]
98
- ) -> CoreSchema:
99
- return cls.schema
100
77
 
101
- @classmethod
102
- def validate(cls, v: Any) -> Self:
103
- return SchemaValidator(schema=cls.schema).validate_python(v)
78
+ class SecretDict(pydantic.Secret[Dict[str, Any]]):
79
+ pass
104
80
 
105
81
 
106
82
  __all__ = [
107
83
  "NonNegativeInteger",
108
84
  "PositiveInteger",
109
85
  "NonNegativeFloat",
110
- "NonNegativeDuration",
111
- "PositiveDuration",
86
+ "Name",
87
+ "NameOrEmpty",
88
+ "NonEmptyishName",
89
+ "SecretDict",
90
+ "StrictVariableValue",
112
91
  ]