prefect 3.7.9.dev2__py3-none-any.whl → 3.7.9.dev4__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/_build_info.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # Generated by versioningit
2
- __version__ = "3.7.9.dev2"
3
- __build_date__ = "2026-07-15 08:52:54.199011+00:00"
4
- __git_commit__ = "4e685996b4633ab80f77d5ec98249fffeb39d36f"
2
+ __version__ = "3.7.9.dev4"
3
+ __build_date__ = "2026-07-18 08:39:23.510683+00:00"
4
+ __git_commit__ = "60025750fb7ea1d69fef8a1c8b1e013cdc8149ae"
5
5
  __dirty__ = False
@@ -158,7 +158,9 @@ def _load_deploy_configs_and_actions(
158
158
  actions: dict[str, Any] = {
159
159
  "build": model.build or [],
160
160
  "push": model.push or [],
161
- "pull": model.pull or [],
161
+ # Preserve the distinction between an omitted/`null` pull (auto-generate a
162
+ # default pull action) and an explicit empty `pull: []` (no pull steps).
163
+ "pull": model.pull,
162
164
  }
163
165
  # Convert Pydantic models to plain dicts for downstream consumption,
164
166
  # excluding keys that were not provided by users to preserve legacy semantics
@@ -90,6 +90,14 @@ async def _run_single_deploy(
90
90
  push_steps = deploy_config.get("push", actions.get("push")) or []
91
91
  pull_steps = deploy_config.get("pull", actions.get("pull")) or []
92
92
 
93
+ # An explicit empty `pull: []` (deployment-level takes precedence over the
94
+ # top-level definition) means "no pull steps". This differs from an
95
+ # omitted/`null` pull, which triggers default pull-action generation.
96
+ effective_pull = (
97
+ deploy_config["pull"] if "pull" in deploy_config else actions.get("pull")
98
+ )
99
+ explicit_no_pull = effective_pull == []
100
+
93
101
  deploy_config = await resolve_block_document_references(deploy_config)
94
102
  deploy_config = await resolve_variables(deploy_config)
95
103
 
@@ -237,6 +245,7 @@ async def _run_single_deploy(
237
245
  ## CONFIGURE PUSH and/or PULL STEPS FOR REMOTE FLOW STORAGE
238
246
  if (
239
247
  is_interactive()
248
+ and not explicit_no_pull
240
249
  and not (deploy_config.get("pull") or actions.get("pull"))
241
250
  and not docker_push_step_exists
242
251
  and confirm(
@@ -255,18 +264,23 @@ async def _run_single_deploy(
255
264
 
256
265
  # Prefer the originally captured pull_steps (taken before resolution) to
257
266
  # preserve unresolved block placeholders in the deployment spec. Only fall
258
- # back to the config/actions/default if no pull steps were provided.
259
- pull_steps = (
260
- pull_steps
261
- or deploy_config.get("pull")
262
- or actions.get("pull")
263
- or await _generate_default_pull_action(
264
- console,
265
- deploy_config=deploy_config,
266
- actions=actions,
267
- is_interactive=is_interactive,
267
+ # back to the config/actions/default if no pull steps were provided. An
268
+ # explicit empty `pull: []` disables pull steps entirely and skips default
269
+ # pull-action generation.
270
+ if explicit_no_pull:
271
+ pull_steps = []
272
+ else:
273
+ pull_steps = (
274
+ pull_steps
275
+ or deploy_config.get("pull")
276
+ or actions.get("pull")
277
+ or await _generate_default_pull_action(
278
+ console,
279
+ deploy_config=deploy_config,
280
+ actions=actions,
281
+ is_interactive=is_interactive,
282
+ )
268
283
  )
269
- )
270
284
 
271
285
  ## RUN BUILD AND PUSH STEPS
272
286
  step_outputs: dict[str, Any] = {}
@@ -438,7 +452,9 @@ async def _run_single_deploy(
438
452
  deploy_config_before_templating,
439
453
  build_steps=build_steps or None,
440
454
  push_steps=push_steps or None,
441
- pull_steps=pull_steps or None,
455
+ # Preserve an explicit empty `pull: []` when saving so the
456
+ # no-pull setting survives; otherwise collapse falsey pull to None.
457
+ pull_steps=pull_steps if explicit_no_pull else (pull_steps or None),
442
458
  triggers=trigger_specs or None,
443
459
  sla=sla_specs or None,
444
460
  prefect_file=prefect_file,
@@ -815,6 +815,7 @@
815
815
  "enum": [
816
816
  "IfNotPresent",
817
817
  "Always",
818
+ "IfPossible",
818
819
  "Never"
819
820
  ],
820
821
  "type": "string"
@@ -10,8 +10,10 @@ state database, they should be the most deeply nested contexts in orchestration
10
10
 
11
11
  from __future__ import annotations
12
12
 
13
+ from datetime import timedelta
13
14
  from typing import Any, Union, cast
14
15
 
16
+ import sqlalchemy as sa
15
17
  from packaging.version import Version
16
18
 
17
19
  import prefect.server.models as models
@@ -66,22 +68,32 @@ class GlobalFlowPolicy(BaseOrchestrationPolicy[orm_models.FlowRun, core.FlowRunP
66
68
  type[BaseOrchestrationRule[orm_models.FlowRun, core.FlowRunPolicy]],
67
69
  ]
68
70
  ]:
69
- return cast(
70
- list[
71
- Union[
72
- type[
73
- BaseUniversalTransform[orm_models.FlowRun, core.FlowRunPolicy]
74
- ],
75
- type[BaseOrchestrationRule[orm_models.FlowRun, core.FlowRunPolicy]],
76
- ]
77
- ],
78
- COMMON_GLOBAL_TRANSFORMS(),
79
- ) + [
80
- UpdateSubflowParentTask,
81
- UpdateSubflowStateDetails,
82
- IncrementFlowRunCount,
83
- RemoveResumingIndicator,
84
- ]
71
+ return (
72
+ [EnsureFlowRunStateTimestampIsUnique]
73
+ + cast(
74
+ list[
75
+ Union[
76
+ type[
77
+ BaseUniversalTransform[
78
+ orm_models.FlowRun, core.FlowRunPolicy
79
+ ]
80
+ ],
81
+ type[
82
+ BaseOrchestrationRule[
83
+ orm_models.FlowRun, core.FlowRunPolicy
84
+ ]
85
+ ],
86
+ ]
87
+ ],
88
+ COMMON_GLOBAL_TRANSFORMS(),
89
+ )
90
+ + [
91
+ UpdateSubflowParentTask,
92
+ UpdateSubflowStateDetails,
93
+ IncrementFlowRunCount,
94
+ RemoveResumingIndicator,
95
+ ]
96
+ )
85
97
 
86
98
 
87
99
  class GlobalTaskPolicy(BaseOrchestrationPolicy[orm_models.TaskRun, core.TaskRunPolicy]):
@@ -112,6 +124,53 @@ class GlobalTaskPolicy(BaseOrchestrationPolicy[orm_models.TaskRun, core.TaskRunP
112
124
  ) + [IncrementTaskRunCount]
113
125
 
114
126
 
127
+ class EnsureFlowRunStateTimestampIsUnique(
128
+ FlowRunUniversalTransform[orm_models.FlowRun, core.FlowRunPolicy]
129
+ ):
130
+ """
131
+ Prevents a flow run's states from sharing a timestamp.
132
+
133
+ State timestamps are minted server-side from the wall clock. On platforms with
134
+ coarse clock resolution (notably Windows, ~15.6ms) several states written for the
135
+ same flow run within a single clock tick can receive identical timestamps,
136
+ violating the unique `(flow_run_id, timestamp)` constraint. When the proposed
137
+ timestamp collides with a persisted state, advance it to the first available
138
+ microsecond after the collision. This handles repeated collisions while keeping
139
+ explicitly backdated states near their intended timestamp.
140
+
141
+ This transform must run before the transforms that read `proposed_state.timestamp`
142
+ (e.g. `SetRunStateTimestamp`, `SetStartTime`, `SetEndTime`, `IncrementRunTime`) so
143
+ the adjusted timestamp is reflected consistently in the persisted state, the run's
144
+ denormalized timestamps, and its runtime accounting.
145
+ """
146
+
147
+ async def before_transition(
148
+ self, context: OrchestrationContext[orm_models.FlowRun, core.FlowRunPolicy]
149
+ ) -> None:
150
+ if self.nullified_transition():
151
+ return
152
+
153
+ proposed_state = context.proposed_state
154
+ if proposed_state is None:
155
+ return
156
+
157
+ candidate_timestamp = proposed_state.timestamp
158
+ while (
159
+ await context.session.scalar(
160
+ sa.select(orm_models.FlowRunState.timestamp)
161
+ .where(
162
+ orm_models.FlowRunState.flow_run_id == context.run.id,
163
+ orm_models.FlowRunState.timestamp == candidate_timestamp,
164
+ )
165
+ .limit(1)
166
+ )
167
+ is not None
168
+ ):
169
+ candidate_timestamp += timedelta(microseconds=1)
170
+
171
+ proposed_state.timestamp = candidate_timestamp
172
+
173
+
115
174
  class SetRunStateType(
116
175
  BaseUniversalTransform[
117
176
  orm_models.Run, Union[core.FlowRunPolicy, core.TaskRunPolicy]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: prefect
3
- Version: 3.7.9.dev2
3
+ Version: 3.7.9.dev4
4
4
  Summary: Workflow orchestration and management.
5
5
  Project-URL: Changelog, https://github.com/PrefectHQ/prefect/releases
6
6
  Project-URL: Documentation, https://docs.prefect.io
@@ -2,7 +2,7 @@ prefect/.prefectignore,sha256=awSprvKT0vI8a64mEOLrMxhxqcO-b0ERQeYpA2rNKVQ,390
2
2
  prefect/AGENTS.md,sha256=H7j73ZMACxsIfHXO6pTAdyQy2yi7XGnADtgwcMqMVO4,11049
3
3
  prefect/__init__.py,sha256=Z8rwfLbEOLh-5WcznTZP3FG2-9UgGZxY-prj8sL0-Qk,6828
4
4
  prefect/__main__.py,sha256=WFjw3kaYJY6pOTA7WDOgqjsz8zUEUZHCcj3P5wyVa-g,66
5
- prefect/_build_info.py,sha256=ScAS7VchF7hyM_2bFqBZJ3lUa1i45rMi1nOtwHErdGY,185
5
+ prefect/_build_info.py,sha256=AWATDlKtJxBAeNA9P88peiYj33uWejBKcXHVJ0EMS9c,185
6
6
  prefect/_flow_run_suspension.py,sha256=5zTTB7ZIBHzoS0pVrhNn23-9hK51qZ3CQA6C-azluC0,4144
7
7
  prefect/agent.py,sha256=dPvG1jDGD5HSH7aM2utwtk6RaJ9qg13XjkA0lAIgQmY,287
8
8
  prefect/artifacts.py,sha256=ZdMLJeJGK82hibtRzbsVa-g95dMa0D2UP1LiESoXmf4,23951
@@ -195,8 +195,8 @@ prefect/cli/cloud/webhook.py,sha256=j0nzupRem5gr1cLutrO5ALhDaiTeNWTBusq1c-UBArI,
195
195
  prefect/cli/deploy/__init__.py,sha256=plVbJQ0ZDPsTFCRL7SYZXUhXaUi0KNU86dlydBeKGjs,121
196
196
  prefect/cli/deploy/_actions.py,sha256=5ORSReuDpdttxsNQTRNyXYl1HxRnCpEut-LVrlaiJ1U,10659
197
197
  prefect/cli/deploy/_commands.py,sha256=ofKDJ0KVTdEZy3BraJoaltKjxhZbDgTrq8xg4dk4IGg,16280
198
- prefect/cli/deploy/_config.py,sha256=d1LEuz1tWmYNqYTp3Gwdly0Pg-Wg-RsEmN5AiiF8jG0,19245
199
- prefect/cli/deploy/_core.py,sha256=cpYKtPiz9imWNhcrlJIT_pHzJdluHHr98AAPMjcUeEg,20554
198
+ prefect/cli/deploy/_config.py,sha256=T9525VOvUT8GP1-If3nGq8NPjToFCm2kW8q4kYEG0bA,19403
199
+ prefect/cli/deploy/_core.py,sha256=Cs9DMBnzlmzfu9cvuNX8qIWy46nnqsTixgyCpBGoHos,21380
200
200
  prefect/cli/deploy/_models.py,sha256=nkS85-OTz4wCzrlKkGqUKwXqHtzyfwbv-0MSC7eHnmE,6102
201
201
  prefect/cli/deploy/_schedules.py,sha256=10uB_aHM-wdWKmHosAm3ycMSRaOshgeF3kr1A0YaZ74,3236
202
202
  prefect/cli/deploy/_sla.py,sha256=1-D0t30kGzs1Jq2hTu2h1kNXS6JKq5t71I_7INI9_bE,1903
@@ -413,7 +413,7 @@ prefect/server/api/validation.py,sha256=DPofXjLFejs_qusUQgzRkG89X2EFoviAy9J08pnN
413
413
  prefect/server/api/variables.py,sha256=8Ursuf20R5zXUS015ZexCH72K7R6Yv76ehkr_l3Xt0k,6186
414
414
  prefect/server/api/work_queues.py,sha256=wEb_Hx1MlTrg5juOguk5nvGKwLmTGSIHEUyt_-2GJiE,11957
415
415
  prefect/server/api/workers.py,sha256=zUxF-ilf-Z9EuXZcqEk-IIbUmZI2v8dpGzhwXayg53Y,46264
416
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json,sha256=otKGMKJUh_ySkQyNBFCe84E6VRbuR-Z7_676D21YKsw,81525
416
+ prefect/server/api/collections_data/views/aggregate-worker-metadata.json,sha256=u5zqD8sY2BNrNoMohlr5Z9xMG0gcwqc5yi_Py4vPO_s,81555
417
417
  prefect/server/api/static/prefect-logo-mark-gradient.png,sha256=ylRjJkI_JHCw8VbQasNnXQHwZW-sH-IQiUGSD3aWP1E,73430
418
418
  prefect/server/api/ui/__init__.py,sha256=TCXO4ZUZCqCbm2QoNvWNTErkzWiX2nSACuO-0Tiomvg,93
419
419
  prefect/server/api/ui/flow_runs.py,sha256=ALmUFY4WrJggN1ha0z-tqXeddG2GptswbPnB7iYixUM,4172
@@ -719,7 +719,7 @@ prefect/server/models/workers.py,sha256=vPqs4dRjVxr_fr9Ibt02o3k4rl7pMhSWr7TfElq6
719
719
  prefect/server/orchestration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
720
720
  prefect/server/orchestration/core_policy.py,sha256=I-BqVUk9x_-GgynatayRFDh8Jri4TM_B73KdpP8EdaI,79896
721
721
  prefect/server/orchestration/dependencies.py,sha256=zV5zUj699gjgGroMVufTOhI5DuRp2r70hNSnQz8i768,4671
722
- prefect/server/orchestration/global_policy.py,sha256=AOGfrZuU7tKwSmRvjzduC-wnnLJiwCOW47kO-h1d5qI,15498
722
+ prefect/server/orchestration/global_policy.py,sha256=lxAE3YeouWmlthTKJuJe8deA-6t8kRbX9VW2OuC5NZw,17807
723
723
  prefect/server/orchestration/instrumentation_policies.py,sha256=Qx5fIzsUyc2cGY00FXksM7o7WRYvjp2daJcPEExDt9M,2416
724
724
  prefect/server/orchestration/policies.py,sha256=Ia18UBA0cFEoI4MJP8t7GmGJBgHr2aSCqrq8YYhuNF4,2794
725
725
  prefect/server/orchestration/rules.py,sha256=33iULZKbNPCtDi8mErcOqh8nOe1YyinqLaOFpfx3he0,44889
@@ -1470,8 +1470,8 @@ prefect/workers/_worker_channel/_protocol.py,sha256=GpCNh1o3qmmqHA_UOOTge1QVC6IR
1470
1470
  prefect/workers/_worker_channel/_state.py,sha256=eQTFZtAVDZH1vVWps3SdeY6aW3qu2wx1UKYQXK3AyuE,5369
1471
1471
  prefect/workers/_worker_channel/_sync.py,sha256=G5G8_UaQYbeLebi5Mb1Z_KgGfXfyjXo5uT9la5_zwqY,14984
1472
1472
  prefect/workers/_worker_channel/_transport.py,sha256=6VXxiZKIu0rqImlif14ojoAL3UGOBTmNpnlYxNggaLE,9423
1473
- prefect-3.7.9.dev2.dist-info/METADATA,sha256=GhBOuGI35nUatpuGsfvG53G91lU1HUN3Nfjli97KdSU,14030
1474
- prefect-3.7.9.dev2.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
1475
- prefect-3.7.9.dev2.dist-info/entry_points.txt,sha256=HlY8up83iIq2vU2r33a0qSis4eOFSyb1mRH4l7Xt9X8,126
1476
- prefect-3.7.9.dev2.dist-info/licenses/LICENSE,sha256=MCxsn8osAkzfxKC4CC_dLcUkU8DZLkyihZ8mGs3Ah3Q,11357
1477
- prefect-3.7.9.dev2.dist-info/RECORD,,
1473
+ prefect-3.7.9.dev4.dist-info/METADATA,sha256=dW9OGkHQXgY_8a8RrpxC3H_pPnkXL9jgp0rQ5fUeD2k,14030
1474
+ prefect-3.7.9.dev4.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
1475
+ prefect-3.7.9.dev4.dist-info/entry_points.txt,sha256=HlY8up83iIq2vU2r33a0qSis4eOFSyb1mRH4l7Xt9X8,126
1476
+ prefect-3.7.9.dev4.dist-info/licenses/LICENSE,sha256=MCxsn8osAkzfxKC4CC_dLcUkU8DZLkyihZ8mGs3Ah3Q,11357
1477
+ prefect-3.7.9.dev4.dist-info/RECORD,,