prefect-client 2.16.1__py3-none-any.whl → 2.16.3__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/events/schemas.py CHANGED
@@ -1,8 +1,22 @@
1
+ import abc
1
2
  from datetime import timedelta
2
- from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Union, cast
3
+ from enum import Enum
4
+ from typing import (
5
+ Any,
6
+ Dict,
7
+ Iterable,
8
+ List,
9
+ Literal,
10
+ Optional,
11
+ Set,
12
+ Tuple,
13
+ Union,
14
+ cast,
15
+ )
3
16
  from uuid import UUID, uuid4
4
17
 
5
18
  import pendulum
19
+ from typing_extensions import TypeAlias
6
20
 
7
21
  from prefect._internal.pydantic import HAS_PYDANTIC_V2
8
22
 
@@ -15,7 +29,6 @@ else:
15
29
 
16
30
  from prefect._internal.schemas.bases import PrefectBaseModel
17
31
  from prefect._internal.schemas.fields import DateTimeTZ
18
- from prefect._internal.schemas.transformations import FieldFrom, copy_model_fields
19
32
  from prefect.events.actions import ActionTypes, RunDeployment
20
33
  from prefect.utilities.collections import AutoEnum
21
34
 
@@ -27,6 +40,7 @@ MAXIMUM_RELATED_RESOURCES = 500
27
40
  class Posture(AutoEnum):
28
41
  Reactive = "Reactive"
29
42
  Proactive = "Proactive"
43
+ Metric = "Metric"
30
44
 
31
45
 
32
46
  class ResourceSpecification(PrefectBaseModel):
@@ -164,32 +178,56 @@ class Event(PrefectBaseModel):
164
178
  return value
165
179
 
166
180
 
167
- class Trigger(PrefectBaseModel):
168
- """Defines the criteria for the events and conditions under which an Automation
169
- will trigger an action"""
181
+ class Trigger(PrefectBaseModel, abc.ABC):
182
+ """
183
+ Base class describing a set of criteria that must be satisfied in order to trigger
184
+ an automation.
185
+ """
186
+
187
+ class Config:
188
+ extra = Extra.ignore
189
+
190
+ type: str
191
+
192
+
193
+ class ResourceTrigger(Trigger, abc.ABC):
194
+ """
195
+ Base class for triggers that may filter by the labels of resources.
196
+ """
197
+
198
+ type: str
170
199
 
171
200
  match: ResourceSpecification = Field(
172
201
  default_factory=lambda: ResourceSpecification(__root__={}),
173
- description="Labels for resources which this Automation will match.",
202
+ description="Labels for resources which this trigger will match.",
174
203
  )
175
204
  match_related: ResourceSpecification = Field(
176
205
  default_factory=lambda: ResourceSpecification(__root__={}),
177
- description="Labels for related resources which this Automation will match.",
206
+ description="Labels for related resources which this trigger will match.",
178
207
  )
179
208
 
209
+
210
+ class EventTrigger(ResourceTrigger):
211
+ """
212
+ A trigger that fires based on the presence or absence of events within a given
213
+ period of time.
214
+ """
215
+
216
+ type: Literal["event"] = "event"
217
+
180
218
  after: Set[str] = Field(
181
219
  default_factory=set,
182
220
  description=(
183
- "The event(s) which must first been seen to start this automation. If "
184
- "empty, then start this Automation immediately. Events may include "
221
+ "The event(s) which must first been seen to fire this trigger. If "
222
+ "empty, then fire this trigger immediately. Events may include "
185
223
  "trailing wildcards, like `prefect.flow-run.*`"
186
224
  ),
187
225
  )
188
226
  expect: Set[str] = Field(
189
227
  default_factory=set,
190
228
  description=(
191
- "The event(s) this automation is expecting to see. If empty, this "
192
- "automation will match any event. Events may include trailing wildcards, "
229
+ "The event(s) this trigger is expecting to see. If empty, this "
230
+ "trigger will match any event. Events may include trailing wildcards, "
193
231
  "like `prefect.flow-run.*`"
194
232
  ),
195
233
  )
@@ -197,24 +235,29 @@ class Trigger(PrefectBaseModel):
197
235
  for_each: Set[str] = Field(
198
236
  default_factory=set,
199
237
  description=(
200
- "Evaluate the Automation separately for each distinct value of these labels"
201
- " on the resource"
238
+ "Evaluate the trigger separately for each distinct value of these labels "
239
+ "on the resource. By default, labels refer to the primary resource of the "
240
+ "triggering event. You may also refer to labels from related "
241
+ "resources by specifying `related:<role>:<label>`. This will use the "
242
+ "value of that label for the first related resource in that role. For "
243
+ 'example, `"for_each": ["related:flow:prefect.resource.id"]` would '
244
+ "evaluate the trigger for each flow."
202
245
  ),
203
246
  )
204
- posture: Posture = Field(
205
- Posture.Reactive,
247
+ posture: Literal[Posture.Reactive, Posture.Proactive] = Field( # type: ignore[valid-type]
248
+ ...,
206
249
  description=(
207
- "The posture of this Automation, either Reactive or Proactive. Reactive "
208
- "automations respond to the _presence_ of the expected events, while "
209
- "Proactive automations respond to the _absence_ of those expected events."
250
+ "The posture of this trigger, either Reactive or Proactive. Reactive "
251
+ "triggers respond to the _presence_ of the expected events, while "
252
+ "Proactive triggers respond to the _absence_ of those expected events."
210
253
  ),
211
254
  )
212
255
  threshold: int = Field(
213
256
  1,
214
257
  description=(
215
- "The number of events required for this Automation to trigger (for "
216
- "Reactive automations), or the number of events expected (for Proactive "
217
- "automations)"
258
+ "The number of events required for this trigger to fire (for "
259
+ "Reactive triggers), or the number of events expected (for Proactive "
260
+ "triggers)"
218
261
  ),
219
262
  )
220
263
  within: timedelta = Field(
@@ -253,6 +296,86 @@ class Trigger(PrefectBaseModel):
253
296
  return values
254
297
 
255
298
 
299
+ class MetricTriggerOperator(Enum):
300
+ LT = "<"
301
+ LTE = "<="
302
+ GT = ">"
303
+ GTE = ">="
304
+
305
+
306
+ class PrefectMetric(Enum):
307
+ lateness = "lateness"
308
+ duration = "duration"
309
+ successes = "successes"
310
+
311
+
312
+ class MetricTriggerQuery(PrefectBaseModel):
313
+ """Defines a subset of the Trigger subclass, which is specific
314
+ to Metric automations, that specify the query configurations
315
+ and breaching conditions for the Automation"""
316
+
317
+ name: PrefectMetric = Field(
318
+ ...,
319
+ description="The name of the metric to query.",
320
+ )
321
+ threshold: float = Field(
322
+ ...,
323
+ description=(
324
+ "The threshold value against which we'll compare " "the query result."
325
+ ),
326
+ )
327
+ operator: MetricTriggerOperator = Field(
328
+ ...,
329
+ description=(
330
+ "The comparative operator (LT / LTE / GT / GTE) used to compare "
331
+ "the query result against the threshold value."
332
+ ),
333
+ )
334
+ range: timedelta = Field(
335
+ timedelta(seconds=300), # defaults to 5 minutes
336
+ minimum=300.0,
337
+ exclusiveMinimum=False,
338
+ description=(
339
+ "The lookback duration (seconds) for a metric query. This duration is "
340
+ "used to determine the time range over which the query will be executed. "
341
+ "The minimum value is 300 seconds (5 minutes)."
342
+ ),
343
+ )
344
+ firing_for: timedelta = Field(
345
+ timedelta(seconds=300), # defaults to 5 minutes
346
+ minimum=300.0,
347
+ exclusiveMinimum=False,
348
+ description=(
349
+ "The duration (seconds) for which the metric query must breach "
350
+ "or resolve continuously before the state is updated and the "
351
+ "automation is triggered. "
352
+ "The minimum value is 300 seconds (5 minutes)."
353
+ ),
354
+ )
355
+
356
+
357
+ class MetricTrigger(ResourceTrigger):
358
+ """
359
+ A trigger that fires based on the results of a metric query.
360
+ """
361
+
362
+ type: Literal["metric"] = "metric"
363
+
364
+ posture: Literal[Posture.Metric] = Field( # type: ignore[valid-type]
365
+ Posture.Metric,
366
+ description="Periodically evaluate the configured metric query.",
367
+ )
368
+
369
+ metric: MetricTriggerQuery = Field(
370
+ ...,
371
+ description="The metric query to evaluate for this trigger. ",
372
+ )
373
+
374
+
375
+ TriggerTypes: TypeAlias = Union[EventTrigger, MetricTrigger]
376
+ """The union of all concrete trigger types that a user may actually create"""
377
+
378
+
256
379
  class Automation(PrefectBaseModel):
257
380
  """Defines an action a user wants to take when a certain number of events
258
381
  do or don't happen to the matching resources"""
@@ -265,7 +388,7 @@ class Automation(PrefectBaseModel):
265
388
 
266
389
  enabled: bool = Field(True, description="Whether this automation will be evaluated")
267
390
 
268
- trigger: Trigger = Field(
391
+ trigger: TriggerTypes = Field(
269
392
  ...,
270
393
  description=(
271
394
  "The criteria for which events this Automation covers and how it will "
@@ -286,30 +409,106 @@ class ExistingAutomation(Automation):
286
409
  id: UUID = Field(..., description="The ID of this automation")
287
410
 
288
411
 
289
- @copy_model_fields
290
- class ResourceTrigger(PrefectBaseModel):
412
+ class AutomationCreateFromTrigger(PrefectBaseModel):
291
413
  name: Optional[str] = Field(
292
414
  None, description="The name to give to the automation created for this trigger."
293
415
  )
294
- description: str = FieldFrom(Automation)
295
- enabled: bool = FieldFrom(Automation)
416
+ description: str = Field("", description="A longer description of this automation")
417
+ enabled: bool = Field(True, description="Whether this automation will be evaluated")
418
+
419
+ # from ResourceTrigger
420
+
421
+ match: ResourceSpecification = Field(
422
+ default_factory=lambda: ResourceSpecification(__root__={}),
423
+ description="Labels for resources which this trigger will match.",
424
+ )
425
+ match_related: ResourceSpecification = Field(
426
+ default_factory=lambda: ResourceSpecification(__root__={}),
427
+ description="Labels for related resources which this trigger will match.",
428
+ )
429
+
430
+ # from both EventTrigger and MetricTrigger
431
+
432
+ posture: Posture = Field(
433
+ Posture.Reactive,
434
+ description=(
435
+ "The posture of this trigger, either Reactive, Proactive, or Metric. "
436
+ "Reactive triggers respond to the _presence_ of the expected events, while "
437
+ "Proactive triggers respond to the _absence_ of those expected events. "
438
+ "Metric triggers periodically evaluate the configured metric query."
439
+ ),
440
+ )
296
441
 
297
- match: ResourceSpecification = FieldFrom(Trigger)
298
- match_related: ResourceSpecification = FieldFrom(Trigger)
299
- after: Set[str] = FieldFrom(Trigger)
300
- expect: Set[str] = FieldFrom(Trigger)
301
- for_each: Set[str] = FieldFrom(Trigger)
302
- posture: Posture = FieldFrom(Trigger)
303
- threshold: int = FieldFrom(Trigger)
304
- within: timedelta = FieldFrom(Trigger)
442
+ # from EventTrigger
443
+
444
+ after: Set[str] = Field(
445
+ default_factory=set,
446
+ description=(
447
+ "The event(s) which must first been seen to fire this trigger. If "
448
+ "empty, then fire this trigger immediately. Events may include "
449
+ "trailing wildcards, like `prefect.flow-run.*`"
450
+ ),
451
+ )
452
+ expect: Set[str] = Field(
453
+ default_factory=set,
454
+ description=(
455
+ "The event(s) this trigger is expecting to see. If empty, this "
456
+ "trigger will match any event. Events may include trailing wildcards, "
457
+ "like `prefect.flow-run.*`"
458
+ ),
459
+ )
460
+
461
+ for_each: Set[str] = Field(
462
+ default_factory=set,
463
+ description=(
464
+ "Evaluate the trigger separately for each distinct value of these labels "
465
+ "on the resource. By default, labels refer to the primary resource of the "
466
+ "triggering event. You may also refer to labels from related "
467
+ "resources by specifying `related:<role>:<label>`. This will use the "
468
+ "value of that label for the first related resource in that role. For "
469
+ 'example, `"for_each": ["related:flow:prefect.resource.id"]` would '
470
+ "evaluate the trigger for each flow."
471
+ ),
472
+ )
473
+ threshold: int = Field(
474
+ 1,
475
+ description=(
476
+ "The number of events required for this trigger to fire (for "
477
+ "Reactive triggers), or the number of events expected (for Proactive "
478
+ "triggers)"
479
+ ),
480
+ )
481
+ within: timedelta = Field(
482
+ timedelta(0),
483
+ minimum=0.0,
484
+ exclusiveMinimum=False,
485
+ description=(
486
+ "The time period over which the events must occur. For Reactive triggers, "
487
+ "this may be as low as 0 seconds, but must be at least 10 seconds for "
488
+ "Proactive triggers"
489
+ ),
490
+ )
491
+
492
+ # from MetricTrigger
493
+
494
+ metric: Optional[MetricTriggerQuery] = Field(
495
+ None,
496
+ description="The metric query to evaluate for this trigger. ",
497
+ )
305
498
 
306
499
  def as_automation(self) -> Automation:
307
500
  assert self.name
308
- return Automation(
309
- name=self.name,
310
- description=self.description,
311
- enabled=self.enabled,
312
- trigger=Trigger(
501
+
502
+ if self.posture == Posture.Metric:
503
+ trigger = MetricTrigger(
504
+ type="metric",
505
+ match=self.match,
506
+ match_related=self.match_related,
507
+ posture=self.posture,
508
+ metric=self.metric,
509
+ )
510
+ else:
511
+ trigger = EventTrigger(
313
512
  match=self.match,
314
513
  match_related=self.match_related,
315
514
  after=self.after,
@@ -318,7 +517,13 @@ class ResourceTrigger(PrefectBaseModel):
318
517
  posture=self.posture,
319
518
  threshold=self.threshold,
320
519
  within=self.within,
321
- ),
520
+ )
521
+
522
+ return Automation(
523
+ name=self.name,
524
+ description=self.description,
525
+ enabled=self.enabled,
526
+ trigger=trigger,
322
527
  actions=self.actions(),
323
528
  owner_resource=self.owner_resource(),
324
529
  )
@@ -330,10 +535,15 @@ class ResourceTrigger(PrefectBaseModel):
330
535
  raise NotImplementedError
331
536
 
332
537
 
333
- @copy_model_fields
334
- class DeploymentTrigger(ResourceTrigger):
538
+ class DeploymentTrigger(AutomationCreateFromTrigger):
335
539
  _deployment_id: Optional[UUID] = PrivateAttr(default=None)
336
- parameters: Optional[Dict[str, Any]] = FieldFrom(RunDeployment)
540
+ parameters: Optional[Dict[str, Any]] = Field(
541
+ None,
542
+ description=(
543
+ "The parameters to pass to the deployment, or None to use the "
544
+ "deployment's default parameters"
545
+ ),
546
+ )
337
547
 
338
548
  def set_deployment_id(self, deployment_id: UUID):
339
549
  self._deployment_id = deployment_id