prefect-client 2.19.3__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.
- prefect/__init__.py +8 -56
- prefect/_internal/compatibility/deprecated.py +6 -115
- prefect/_internal/compatibility/experimental.py +4 -79
- prefect/_internal/concurrency/api.py +0 -34
- prefect/_internal/concurrency/calls.py +0 -6
- prefect/_internal/concurrency/cancellation.py +0 -3
- prefect/_internal/concurrency/event_loop.py +0 -20
- prefect/_internal/concurrency/inspection.py +3 -3
- prefect/_internal/concurrency/threads.py +35 -0
- prefect/_internal/concurrency/waiters.py +0 -28
- prefect/_internal/pydantic/__init__.py +0 -45
- prefect/_internal/pydantic/v1_schema.py +21 -22
- prefect/_internal/pydantic/v2_schema.py +0 -2
- prefect/_internal/pydantic/v2_validated_func.py +18 -23
- prefect/_internal/schemas/bases.py +44 -177
- prefect/_internal/schemas/fields.py +1 -43
- prefect/_internal/schemas/validators.py +60 -158
- prefect/artifacts.py +161 -14
- prefect/automations.py +39 -4
- prefect/blocks/abstract.py +1 -1
- prefect/blocks/core.py +268 -148
- prefect/blocks/fields.py +2 -57
- prefect/blocks/kubernetes.py +8 -12
- prefect/blocks/notifications.py +40 -20
- prefect/blocks/system.py +22 -11
- prefect/blocks/webhook.py +2 -9
- prefect/client/base.py +4 -4
- prefect/client/cloud.py +8 -13
- prefect/client/orchestration.py +347 -341
- prefect/client/schemas/actions.py +92 -86
- prefect/client/schemas/filters.py +20 -40
- prefect/client/schemas/objects.py +147 -145
- prefect/client/schemas/responses.py +16 -24
- prefect/client/schemas/schedules.py +47 -35
- prefect/client/subscriptions.py +2 -2
- prefect/client/utilities.py +5 -2
- prefect/concurrency/asyncio.py +3 -1
- prefect/concurrency/events.py +1 -1
- prefect/concurrency/services.py +6 -3
- prefect/context.py +195 -27
- prefect/deployments/__init__.py +5 -6
- prefect/deployments/base.py +7 -5
- prefect/deployments/flow_runs.py +185 -0
- prefect/deployments/runner.py +50 -45
- prefect/deployments/schedules.py +28 -23
- prefect/deployments/steps/__init__.py +0 -1
- prefect/deployments/steps/core.py +1 -0
- prefect/deployments/steps/pull.py +7 -21
- prefect/engine.py +12 -2422
- prefect/events/actions.py +17 -23
- prefect/events/cli/automations.py +19 -6
- prefect/events/clients.py +14 -37
- prefect/events/filters.py +14 -18
- prefect/events/related.py +2 -2
- prefect/events/schemas/__init__.py +0 -5
- prefect/events/schemas/automations.py +55 -46
- prefect/events/schemas/deployment_triggers.py +7 -197
- prefect/events/schemas/events.py +34 -65
- prefect/events/schemas/labelling.py +10 -14
- prefect/events/utilities.py +2 -3
- prefect/events/worker.py +2 -3
- prefect/filesystems.py +6 -517
- prefect/{new_flow_engine.py → flow_engine.py} +313 -72
- prefect/flow_runs.py +377 -5
- prefect/flows.py +248 -165
- prefect/futures.py +186 -345
- prefect/infrastructure/__init__.py +0 -27
- prefect/infrastructure/provisioners/__init__.py +5 -3
- prefect/infrastructure/provisioners/cloud_run.py +11 -6
- prefect/infrastructure/provisioners/container_instance.py +11 -7
- prefect/infrastructure/provisioners/ecs.py +6 -4
- prefect/infrastructure/provisioners/modal.py +8 -5
- prefect/input/actions.py +2 -4
- prefect/input/run_input.py +5 -7
- prefect/logging/formatters.py +0 -2
- prefect/logging/handlers.py +3 -11
- prefect/logging/loggers.py +2 -2
- prefect/manifests.py +2 -1
- prefect/records/__init__.py +1 -0
- prefect/records/result_store.py +42 -0
- prefect/records/store.py +9 -0
- prefect/results.py +43 -39
- prefect/runner/runner.py +9 -9
- prefect/runner/server.py +6 -10
- prefect/runner/storage.py +3 -8
- prefect/runner/submit.py +2 -2
- prefect/runner/utils.py +2 -2
- prefect/serializers.py +24 -35
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
- prefect/settings.py +70 -133
- prefect/states.py +17 -47
- prefect/task_engine.py +697 -58
- prefect/task_runners.py +269 -301
- prefect/task_server.py +53 -34
- prefect/tasks.py +327 -337
- prefect/transactions.py +220 -0
- prefect/types/__init__.py +61 -82
- prefect/utilities/asyncutils.py +195 -136
- prefect/utilities/callables.py +121 -41
- prefect/utilities/collections.py +23 -38
- prefect/utilities/dispatch.py +11 -3
- prefect/utilities/dockerutils.py +4 -0
- prefect/utilities/engine.py +140 -20
- prefect/utilities/importtools.py +26 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +5 -1
- prefect/utilities/templating.py +12 -2
- prefect/variables.py +78 -61
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +15 -17
- prefect/workers/process.py +3 -8
- prefect/workers/server.py +2 -2
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/METADATA +22 -21
- prefect_client-3.0.0rc1.dist-info/RECORD +176 -0
- prefect/_internal/pydantic/_base_model.py +0 -51
- prefect/_internal/pydantic/_compat.py +0 -82
- prefect/_internal/pydantic/_flags.py +0 -20
- prefect/_internal/pydantic/_types.py +0 -8
- prefect/_internal/pydantic/utilities/__init__.py +0 -0
- prefect/_internal/pydantic/utilities/config_dict.py +0 -72
- prefect/_internal/pydantic/utilities/field_validator.py +0 -150
- prefect/_internal/pydantic/utilities/model_construct.py +0 -56
- prefect/_internal/pydantic/utilities/model_copy.py +0 -55
- prefect/_internal/pydantic/utilities/model_dump.py +0 -136
- prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
- prefect/_internal/pydantic/utilities/model_fields.py +0 -50
- prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
- prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
- prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
- prefect/_internal/pydantic/utilities/model_validate.py +0 -75
- prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
- prefect/_internal/pydantic/utilities/model_validator.py +0 -87
- prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
- prefect/_vendor/__init__.py +0 -0
- prefect/_vendor/fastapi/__init__.py +0 -25
- prefect/_vendor/fastapi/applications.py +0 -946
- prefect/_vendor/fastapi/background.py +0 -3
- prefect/_vendor/fastapi/concurrency.py +0 -44
- prefect/_vendor/fastapi/datastructures.py +0 -58
- prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
- prefect/_vendor/fastapi/dependencies/models.py +0 -64
- prefect/_vendor/fastapi/dependencies/utils.py +0 -877
- prefect/_vendor/fastapi/encoders.py +0 -177
- prefect/_vendor/fastapi/exception_handlers.py +0 -40
- prefect/_vendor/fastapi/exceptions.py +0 -46
- prefect/_vendor/fastapi/logger.py +0 -3
- prefect/_vendor/fastapi/middleware/__init__.py +0 -1
- prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
- prefect/_vendor/fastapi/middleware/cors.py +0 -3
- prefect/_vendor/fastapi/middleware/gzip.py +0 -3
- prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
- prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
- prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
- prefect/_vendor/fastapi/openapi/__init__.py +0 -0
- prefect/_vendor/fastapi/openapi/constants.py +0 -2
- prefect/_vendor/fastapi/openapi/docs.py +0 -203
- prefect/_vendor/fastapi/openapi/models.py +0 -480
- prefect/_vendor/fastapi/openapi/utils.py +0 -485
- prefect/_vendor/fastapi/param_functions.py +0 -340
- prefect/_vendor/fastapi/params.py +0 -453
- prefect/_vendor/fastapi/requests.py +0 -4
- prefect/_vendor/fastapi/responses.py +0 -40
- prefect/_vendor/fastapi/routing.py +0 -1331
- prefect/_vendor/fastapi/security/__init__.py +0 -15
- prefect/_vendor/fastapi/security/api_key.py +0 -98
- prefect/_vendor/fastapi/security/base.py +0 -6
- prefect/_vendor/fastapi/security/http.py +0 -172
- prefect/_vendor/fastapi/security/oauth2.py +0 -227
- prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
- prefect/_vendor/fastapi/security/utils.py +0 -10
- prefect/_vendor/fastapi/staticfiles.py +0 -1
- prefect/_vendor/fastapi/templating.py +0 -3
- prefect/_vendor/fastapi/testclient.py +0 -1
- prefect/_vendor/fastapi/types.py +0 -3
- prefect/_vendor/fastapi/utils.py +0 -235
- prefect/_vendor/fastapi/websockets.py +0 -7
- prefect/_vendor/starlette/__init__.py +0 -1
- prefect/_vendor/starlette/_compat.py +0 -28
- prefect/_vendor/starlette/_exception_handler.py +0 -80
- prefect/_vendor/starlette/_utils.py +0 -88
- prefect/_vendor/starlette/applications.py +0 -261
- prefect/_vendor/starlette/authentication.py +0 -159
- prefect/_vendor/starlette/background.py +0 -43
- prefect/_vendor/starlette/concurrency.py +0 -59
- prefect/_vendor/starlette/config.py +0 -151
- prefect/_vendor/starlette/convertors.py +0 -87
- prefect/_vendor/starlette/datastructures.py +0 -707
- prefect/_vendor/starlette/endpoints.py +0 -130
- prefect/_vendor/starlette/exceptions.py +0 -60
- prefect/_vendor/starlette/formparsers.py +0 -276
- prefect/_vendor/starlette/middleware/__init__.py +0 -17
- prefect/_vendor/starlette/middleware/authentication.py +0 -52
- prefect/_vendor/starlette/middleware/base.py +0 -220
- prefect/_vendor/starlette/middleware/cors.py +0 -176
- prefect/_vendor/starlette/middleware/errors.py +0 -265
- prefect/_vendor/starlette/middleware/exceptions.py +0 -74
- prefect/_vendor/starlette/middleware/gzip.py +0 -113
- prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
- prefect/_vendor/starlette/middleware/sessions.py +0 -82
- prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
- prefect/_vendor/starlette/middleware/wsgi.py +0 -147
- prefect/_vendor/starlette/requests.py +0 -328
- prefect/_vendor/starlette/responses.py +0 -347
- prefect/_vendor/starlette/routing.py +0 -933
- prefect/_vendor/starlette/schemas.py +0 -154
- prefect/_vendor/starlette/staticfiles.py +0 -248
- prefect/_vendor/starlette/status.py +0 -199
- prefect/_vendor/starlette/templating.py +0 -231
- prefect/_vendor/starlette/testclient.py +0 -804
- prefect/_vendor/starlette/types.py +0 -30
- prefect/_vendor/starlette/websockets.py +0 -193
- prefect/agent.py +0 -698
- prefect/deployments/deployments.py +0 -1042
- prefect/deprecated/__init__.py +0 -0
- prefect/deprecated/data_documents.py +0 -350
- prefect/deprecated/packaging/__init__.py +0 -12
- prefect/deprecated/packaging/base.py +0 -96
- prefect/deprecated/packaging/docker.py +0 -146
- prefect/deprecated/packaging/file.py +0 -92
- prefect/deprecated/packaging/orion.py +0 -80
- prefect/deprecated/packaging/serializers.py +0 -171
- prefect/events/instrument.py +0 -135
- prefect/infrastructure/base.py +0 -323
- prefect/infrastructure/container.py +0 -818
- prefect/infrastructure/kubernetes.py +0 -920
- prefect/infrastructure/process.py +0 -289
- prefect/new_task_engine.py +0 -423
- prefect/pydantic/__init__.py +0 -76
- prefect/pydantic/main.py +0 -39
- prefect/software/__init__.py +0 -2
- prefect/software/base.py +0 -50
- prefect/software/conda.py +0 -199
- prefect/software/pip.py +0 -122
- prefect/software/python.py +0 -52
- prefect/workers/block.py +0 -218
- prefect_client-2.19.3.dist-info/RECORD +0 -292
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/LICENSE +0 -0
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/WHEEL +0 -0
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/top_level.txt +0 -0
prefect/transactions.py
ADDED
@@ -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,
|
1
|
+
from typing import Annotated, Any, Dict, List, Union
|
2
|
+
import orjson
|
3
|
+
import pydantic
|
2
4
|
|
3
|
-
from
|
4
|
-
|
5
|
-
|
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
|
-
|
9
|
-
"""An integer that must be greater than or equal to 0."""
|
18
|
+
timezone_set = available_timezones()
|
10
19
|
|
11
|
-
|
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
|
-
|
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
|
-
|
24
|
-
|
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
|
-
|
35
|
+
def non_emptyish(value: str) -> str:
|
36
|
+
if not value.strip("' \""):
|
37
|
+
raise ValueError("name cannot be an empty string")
|
32
38
|
|
33
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
73
|
+
StrictVariableValue = Annotated[VariableValue, BeforeValidator(check_variable_value)]
|
90
74
|
|
91
|
-
|
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
|
-
|
102
|
-
|
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
|
-
"
|
111
|
-
"
|
86
|
+
"Name",
|
87
|
+
"NameOrEmpty",
|
88
|
+
"NonEmptyishName",
|
89
|
+
"SecretDict",
|
90
|
+
"StrictVariableValue",
|
112
91
|
]
|