Skip to content

Commit

Permalink
clean up types
Browse files Browse the repository at this point in the history
  • Loading branch information
zzstoatzz committed Nov 23, 2024
1 parent d64c0d7 commit 457140b
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 32 deletions.
22 changes: 22 additions & 0 deletions docs/v3/develop/settings-ref.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ If True, enables debug mode which may provide additional logging and debugging f

**TOML dotted key path**: `deployments`

### `events`
Settings for controlling events behavior

**Type**: [EventsSettings](#eventssettings)

**TOML dotted key path**: `events`

### `experiments`
Settings for controlling experimental features

Expand Down Expand Up @@ -440,6 +447,21 @@ The default Docker namespace to use when building images.
**Supported environment variables**:
`PREFECT_DEPLOYMENTS_DEFAULT_DOCKER_BUILD_NAMESPACE`, `PREFECT_DEFAULT_DOCKER_BUILD_NAMESPACE`

---
## EventsSettings
Settings for controlling behavior of the events subsystem
### `maximum_event_name_length`
The maximum length of an event name.

**Type**: `integer`

**Default**: `1024`

**TOML dotted key path**: `events.maximum_event_name_length`

**Supported environment variables**:
`PREFECT_EVENTS_MAXIMUM_EVENT_NAME_LENGTH`

---
## ExperimentsSettings
Settings for configuring experimental features
Expand Down
21 changes: 21 additions & 0 deletions schemas/settings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,22 @@
"title": "DeploymentsSettings",
"type": "object"
},
"EventsSettings": {
"description": "Settings for controlling behavior of the events subsystem",
"properties": {
"maximum_event_name_length": {
"default": 1024,
"description": "The maximum length of an event name.",
"supported_environment_variables": [
"PREFECT_EVENTS_MAXIMUM_EVENT_NAME_LENGTH"
],
"title": "Maximum Event Name Length",
"type": "integer"
}
},
"title": "EventsSettings",
"type": "object"
},
"ExperimentsSettings": {
"description": "Settings for configuring experimental features",
"properties": {
Expand Down Expand Up @@ -2173,6 +2189,11 @@
"$ref": "#/$defs/DeploymentsSettings",
"supported_environment_variables": []
},
"events": {
"$ref": "#/$defs/EventsSettings",
"description": "Settings for controlling events behavior",
"supported_environment_variables": []
},
"experiments": {
"$ref": "#/$defs/ExperimentsSettings",
"description": "Settings for controlling experimental features",
Expand Down
37 changes: 19 additions & 18 deletions src/prefect/events/schemas/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,27 @@
)
from uuid import UUID, uuid4

import pendulum
from pydantic import ConfigDict, Field, RootModel, field_validator, model_validator
from pydantic import (
AfterValidator,
BeforeValidator,
ConfigDict,
Field,
RootModel,
model_validator,
)
from pydantic_extra_types.pendulum_dt import DateTime
from typing_extensions import Self
from typing_extensions import Annotated, Self

from prefect._internal.schemas.bases import PrefectBaseModel
from prefect.logging import get_logger
from prefect.settings import (
PREFECT_EVENTS_MAXIMUM_LABELS_PER_RESOURCE,
PREFECT_EVENTS_MAXIMUM_RELATED_RESOURCES,
)

from ..validators import (
_validate_event_length,
_validate_related_resources,
)
from .labelling import Labelled

logger = get_logger(__name__)
Expand Down Expand Up @@ -96,16 +105,19 @@ class Event(PrefectBaseModel):
model_config = ConfigDict(extra="ignore")

occurred: DateTime = Field(
default_factory=lambda: pendulum.now("UTC"),
default_factory=lambda: DateTime.now("UTC"),
description="When the event happened from the sender's perspective",
)
event: str = Field(
event: Annotated[str, BeforeValidator(_validate_event_length)] = Field(
description="The name of the event that happened",
)
resource: Resource = Field(
description="The primary Resource this event concerns",
)
related: List[RelatedResource] = Field(
related: Annotated[
List[RelatedResource],
AfterValidator(_validate_related_resources),
] = Field(
default_factory=list,
description="A list of additional Resources involved in this event",
)
Expand Down Expand Up @@ -144,17 +156,6 @@ def resources_in_role(self) -> Mapping[str, Sequence[RelatedResource]]:
resources[related.role].append(related)
return resources

@field_validator("related")
@classmethod
def enforce_maximum_related_resources(cls, value: List[RelatedResource]):
if len(value) > PREFECT_EVENTS_MAXIMUM_RELATED_RESOURCES.value():
raise ValueError(
"The maximum number of related resources "
f"is {PREFECT_EVENTS_MAXIMUM_RELATED_RESOURCES.value()}"
)

return value

def find_resource_label(self, label: str) -> Optional[str]:
"""Finds the value of the given label in this event's resource or one of its
related resources. If the label starts with `related:<role>:`, search for the
Expand Down
22 changes: 22 additions & 0 deletions src/prefect/events/validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import List


def _validate_event_length(value: str) -> str:
from prefect.settings import PREFECT_EVENTS_MAXIMUM_EVENT_NAME_LENGTH

if len(value) > PREFECT_EVENTS_MAXIMUM_EVENT_NAME_LENGTH.value():
raise ValueError(
f"Event name must be at most {PREFECT_EVENTS_MAXIMUM_EVENT_NAME_LENGTH.value()} characters"
)
return value


def _validate_related_resources(value) -> List:
from prefect.settings import PREFECT_EVENTS_MAXIMUM_RELATED_RESOURCES

if len(value) > PREFECT_EVENTS_MAXIMUM_RELATED_RESOURCES.value():
raise ValueError(
"The maximum number of related resources "
f"is {PREFECT_EVENTS_MAXIMUM_RELATED_RESOURCES.value()}"
)
return value
20 changes: 20 additions & 0 deletions src/prefect/settings/models/events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from pydantic import AliasChoices, AliasPath, Field

from prefect.settings.base import PrefectBaseSettings, _build_settings_config


class EventsSettings(PrefectBaseSettings):
"""
Settings for controlling behavior of the events subsystem
"""

model_config = _build_settings_config(("events",))

maximum_event_name_length: int = Field(
default=1024,
description="The maximum length of an event name.",
validation_alias=AliasChoices(
AliasPath("maximum_event_name_length"),
"prefect_events_maximum_event_name_length",
),
)
6 changes: 6 additions & 0 deletions src/prefect/settings/models/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from .client import ClientSettings
from .cloud import CloudSettings
from .deployments import DeploymentsSettings
from .events import EventsSettings
from .experiments import ExperimentsSettings
from .flows import FlowsSettings
from .internal import InternalSettings
Expand Down Expand Up @@ -85,6 +86,11 @@ class Settings(PrefectBaseSettings):
description="Settings for configuring deployments defaults",
)

events: EventsSettings = Field(
default_factory=EventsSettings,
description="Settings for controlling events behavior",
)

experiments: ExperimentsSettings = Field(
default_factory=ExperimentsSettings,
description="Settings for controlling experimental features",
Expand Down
5 changes: 1 addition & 4 deletions src/prefect/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def cast_none_to_empty_dict(value: Any) -> dict[str, Any]:


KeyValueLabels = Annotated[
dict[str, Union[StrictBool, StrictInt, StrictFloat, str]],
Dict[str, Union[StrictBool, StrictInt, StrictFloat, str]],
BeforeValidator(cast_none_to_empty_dict),
]

Expand Down Expand Up @@ -149,9 +149,6 @@ def validate_set_T_from_delim_string(
]


KeyValueLabels: TypeAlias = dict[str, Union[StrictBool, StrictInt, StrictFloat, str]]


def convert_none_to_empty_dict(v: Optional[KeyValueLabels]) -> KeyValueLabels:
return v or {}

Expand Down
19 changes: 9 additions & 10 deletions tests/events/client/test_events_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
from datetime import timezone
from uuid import UUID, uuid4

import pendulum
import pytest
from pendulum.datetime import DateTime
from pydantic_extra_types.pendulum_dt import DateTime

from prefect.events import Event, RelatedResource, Resource

Expand All @@ -19,12 +18,12 @@ def test_client_events_generate_an_id_by_default():

def test_client_events_generate_occurred_by_default(start_of_test: DateTime):
event = Event(event="hello", resource={"prefect.resource.id": "hello"})
assert start_of_test <= event.occurred <= pendulum.now("UTC")
assert start_of_test <= event.occurred <= DateTime.now("UTC")


def test_client_events_may_have_empty_related_resources():
event = Event(
occurred=pendulum.now("UTC"),
occurred=DateTime.now("UTC"),
event="hello",
resource={"prefect.resource.id": "hello"},
id=uuid4(),
Expand All @@ -34,7 +33,7 @@ def test_client_events_may_have_empty_related_resources():

def test_client_event_resources_have_correct_types():
event = Event(
occurred=pendulum.now("UTC"),
occurred=DateTime.now("UTC"),
event="hello",
resource={"prefect.resource.id": "hello"},
related=[
Expand All @@ -49,7 +48,7 @@ def test_client_event_resources_have_correct_types():

def test_client_events_may_have_multiple_related_resources():
event = Event(
occurred=pendulum.now("UTC"),
occurred=DateTime.now("UTC"),
event="hello",
resource={"prefect.resource.id": "hello"},
related=[
Expand All @@ -69,7 +68,7 @@ def test_client_events_may_have_multiple_related_resources():

def test_json_representation():
event = Event(
occurred=pendulum.DateTime(2021, 2, 3, 4, 5, 6, 7, tzinfo=timezone.utc),
occurred=DateTime(2021, 2, 3, 4, 5, 6, 7, tzinfo=timezone.utc),
event="hello",
resource={"prefect.resource.id": "hello"},
related=[
Expand Down Expand Up @@ -101,7 +100,7 @@ def test_json_representation():

def test_client_event_involved_resources():
event = Event(
occurred=pendulum.now("UTC"),
occurred=DateTime.now("UTC"),
event="hello",
resource={"prefect.resource.id": "hello"},
related=[
Expand All @@ -118,7 +117,7 @@ def test_client_event_involved_resources():

def test_client_events_may_have_a_name_label():
event = Event(
occurred=pendulum.now("UTC"),
occurred=DateTime.now("UTC"),
event="hello",
resource={"prefect.resource.id": "hello", "prefect.resource.name": "Hello!"},
related=[
Expand Down Expand Up @@ -151,7 +150,7 @@ def test_client_events_may_have_a_name_label():
@pytest.fixture
def example_event() -> Event:
return Event(
occurred=pendulum.now("UTC"),
occurred=DateTime.now("UTC"),
event="hello",
resource={
"prefect.resource.id": "hello",
Expand Down
1 change: 1 addition & 0 deletions tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@
"test_value": timedelta(seconds=60),
"legacy": True,
},
"PREFECT_EVENTS_MAXIMUM_EVENT_NAME_LENGTH": {"test_value": 1024},
"PREFECT_EVENTS_MAXIMUM_LABELS_PER_RESOURCE": {"test_value": 10, "legacy": True},
"PREFECT_EVENTS_MAXIMUM_RELATED_RESOURCES": {"test_value": 10, "legacy": True},
"PREFECT_EVENTS_MAXIMUM_SIZE_BYTES": {"test_value": 10, "legacy": True},
Expand Down

0 comments on commit 457140b

Please sign in to comment.