Module adcp.types.canonical_creative

Canonical-first creative models for the Python 7 public API.

The generated protocol models intentionally remain wire-faithful through the AdCP 3.x transition and therefore contain legacy named-format identity. They are exposed from :mod:adcp.types.legacy. This module provides the primary application-facing models: legacy identity is absent from their declared fields, JSON Schema, and serialized output at every nesting depth.

format_kind is a str, not the generated enum — a deliberate override

core/canonical-format-kind.json declares a closed 16-member enum and, in the same file, states as normative:

Consumer SDKs MUST treat this enum as **open** at parse time: an unknown
<code>format\_kind</code> value MUST be retained as-is on the in-memory object (not
silently dropped or rewritten to ``"custom"``) and MUST NOT cause the
surrounding payload to fail validation. ... The producer-side enum stays
closed (sellers MUST NOT mint ad-hoc <code>format\_kind</code> values ...); the
consumer-side enum stays open for forward compatibility.

The schema already knows the rule is directional, and then encodes it as a single closed enum, which cannot carry that. So this override implements what the schema says rather than contradicting it: every reference to that schema generates a bare str — OPEN_VOCABULARY_SCHEMAS in scripts/generate_types.py, one schema-level transform rather than a widening at each call site.

The SDK refuses nothing, in either direction, and that is deliberate. A seller supports some set of format kinds; that set is the seller's, not this library's and not the pinned enum's. It can be larger — the seller handles a kind promoted in a spec newer than the pin — or smaller, four of the sixteen. A pinned SDK cannot tell "a kind the seller invented" from "a kind defined after my pin": both are simply "not in my 16". Refusing the second to prevent the first would make this SDK's version a ceiling on what the protocol permits, which is the same defect as the closed enum with the enforcement moved into a validator. The producer-side MUST is a seller's obligation; this library gives it the vocabulary and :func:is_canonical_format_kind() to meet it, and leaves the decision where the knowledge is.

So the vocabulary is not discarded, it is relocated: :class:CanonicalFormatKind stays a first-class export, used for comparison (creative.format_kind == CanonicalFormatKind.image — it is a StrEnum, so that holds against a plain string field and no adopter writes a literal) and for membership through :func:is_canonical_format_kind(), whose vocabulary is a parameter. What changed is only that the field no longer refuses the seventeenth value a newer server sends. That is the registry pattern the sibling fields already use: format_shape and asset_group_id are plain strings governed by versioned registries whose own governance says non-canonical values stay valid and validators may warn.

The one cost is that the generated models no longer agree with the bundled schema for this one field. That is declared, by name and with this reason, as a single entry in tests/conformance/_schema_parity.py's DECLARED — format_kind_is_an_open_vocabulary_by_decision. The parity rule itself is not relaxed.

Upstream ask: adcontextprotocol/adcp#7929 <https://github.com/adcontextprotocol/adcp/issues/7929>_. If it lands — the schema stating the directional rule in a form that can carry it, the way format_shape is governed — delete the transform, delete this section, and the generated models go back to agreeing with their schema.

Functions

def is_canonical_format_kind(value: object,
vocabulary: Iterable[str] = adcp.types.domains.core.canonical_format_kind.CanonicalFormatKind) ‑> bool
Expand source code
def is_canonical_format_kind(
    value: object,
    vocabulary: Iterable[str] = CanonicalFormatKind,
) -> bool:
    """Is *value* one of *vocabulary*'s format kinds?

    The vocabulary defaults to :class:`CanonicalFormatKind`, the sixteen kinds
    the pinned AdCP bundle declares — but it is a PARAMETER, because the set
    that matters is the seller's, not this SDK's. A seller may support a kind
    promoted in a spec newer than the pin, or only four of the sixteen, and
    neither is expressible by anything this library knows.

    **The SDK never calls this for you.** ``format_kind`` is a ``str``
    everywhere, on the way out and on the way back, and no model refuses a
    value. That is deliberate: a pinned library cannot tell "a kind the seller
    invented" from "a kind defined after my pin", so refusing the second to
    prevent the first would make this SDK's version a ceiling on what the
    protocol permits. "I accept the request and then tell you I cannot process
    this creative" is a seller's answer, not a type error.

    This function is the sanctioned way to be strict, where the caller knows
    which spec version its counterpart speaks::

        from adcp.types import is_canonical_format_kind

        if not is_canonical_format_kind(creative.format_kind):
            route_as_declared_but_unsupported(creative)

        if not is_canonical_format_kind(manifest.format_kind, MY_SUPPORTED_KINDS):
            reject_with_unsupported_format(manifest)

    Comparison needs no helper: ``CanonicalFormatKind`` is a ``StrEnum``, so
    ``creative.format_kind == CanonicalFormatKind.image`` holds against a plain
    string field and an adopter never writes a literal.
    """

    return isinstance(value, str) and any(value == kind for kind in vocabulary)

Is value one of vocabulary's format kinds?

The vocabulary defaults to :class:CanonicalFormatKind, the sixteen kinds the pinned AdCP bundle declares — but it is a PARAMETER, because the set that matters is the seller's, not this SDK's. A seller may support a kind promoted in a spec newer than the pin, or only four of the sixteen, and neither is expressible by anything this library knows.

The SDK never calls this for you. format_kind is a str everywhere, on the way out and on the way back, and no model refuses a value. That is deliberate: a pinned library cannot tell "a kind the seller invented" from "a kind defined after my pin", so refusing the second to prevent the first would make this SDK's version a ceiling on what the protocol permits. "I accept the request and then tell you I cannot process this creative" is a seller's answer, not a type error.

This function is the sanctioned way to be strict, where the caller knows which spec version its counterpart speaks::

from adcp.types import is_canonical_format_kind

if not is_canonical_format_kind(creative.format_kind):
    route_as_declared_but_unsupported(creative)

if not is_canonical_format_kind(manifest.format_kind, MY_SUPPORTED_KINDS):
    reject_with_unsupported_format(manifest)

Comparison needs no helper: CanonicalFormatKind is a StrEnum, so creative.format_kind == CanonicalFormatKind.image holds against a plain string field and an adopter never writes a literal.

def is_legacy_creative_identity_key(key: object) ‑> bool
Expand source code
def is_legacy_creative_identity_key(key: object) -> bool:
    """Return whether *key* names legacy creative routing identity."""

    return isinstance(key, str) and bool(_LEGACY_IDENTITY_KEY.search(key))

Return whether key names legacy creative routing identity.

def sanitize_canonical_schema(schema: dict[str, Any]) ‑> dict[str, typing.Any]
Expand source code
def sanitize_canonical_schema(schema: dict[str, Any]) -> dict[str, Any]:
    """Return a defensive deep copy with legacy identity removed."""

    sanitized: dict[str, Any] = _sanitize_schema_node(copy.deepcopy(schema))
    return sanitized

Return a defensive deep copy with legacy identity removed.

def strip_legacy_creative_identity(value: Any) ‑> Any
Expand source code
def strip_legacy_creative_identity(
    value: Any,
    *,
    _path: str = "$",
    _format_scope: bool = False,
) -> Any:
    """Recursively remove legacy creative identity from a serialized value.

    This is deliberately a runtime boundary rather than a typing convention.
    Unknown extension bags are traversed too, so ``extra='allow'`` can never be
    used to smuggle ``format_id`` or ``format_ids`` through a primary model.
    """

    if isinstance(value, dict):
        legacy_tuple = _is_format_scoped_agent_tuple(
            value,
            path=_path,
        )
        return {
            key: strip_legacy_creative_identity(
                item,
                _path=f"{_path}.{key}",
                _format_scope=_format_scope or (isinstance(key, str) and "format" in key.lower()),
            )
            for key, item in value.items()
            if not is_legacy_creative_identity_key(key)
            and not (legacy_tuple and key == "agent_url")
        }
    if isinstance(value, list):
        return [
            strip_legacy_creative_identity(
                item,
                _path=f"{_path}[{index}]",
                _format_scope=_format_scope,
            )
            for index, item in enumerate(value)
        ]
    if isinstance(value, tuple):
        return tuple(
            strip_legacy_creative_identity(
                item,
                _path=f"{_path}[{index}]",
                _format_scope=_format_scope,
            )
            for index, item in enumerate(value)
        )
    return value

Recursively remove legacy creative identity from a serialized value.

This is deliberately a runtime boundary rather than a typing convention. Unknown extension bags are traversed too, so extra='allow' can never be used to smuggle format_id or format_ids through a primary model.

Classes

class CanonicalBoundaryModel (**data: Any)
Expand source code
class CanonicalBoundaryModel(AdCPBaseModel):
    """Base class enforcing the primary canonical runtime boundary.

    Every canonical model is a real subclass of the generated wire model it
    refines, so each one inherits the generated model's fields, its injected
    ``model_validator``s, and its envelope ancestry. Two concerns that used to
    be re-attached per class by ``create_model`` are therefore declared once,
    here, and inherited:

    * ``_serialize_canonical`` — the wrap serializer that strips legacy creative
      identity from nested and ``TypeAdapter`` serialization.
    * ``__pydantic_init_subclass__`` — the one declared field removal. Legacy
      creative identity must be absent from a canonical model's *declared*
      fields, which is the single thing inheritance alone cannot express; the
      rule reads the same :func:`is_legacy_creative_identity_key` predicate that
      governs the input validator, the schema sanitizer and the serializer, so
      there is one strip predicate for all four.

    ``CanonicalBoundaryModel`` is listed LAST among a canonical model's bases.
    Pydantic merges ``model_config`` across bases left to right, so the
    right-most base wins; the generated wire model inherits
    :class:`AdCPBaseModel`'s ``extra`` policy and would otherwise override this
    class's ``extra="allow"`` and start dropping caller-supplied extension keys.
    """

    model_config = ConfigDict(extra="allow", defer_build=True)
    __adcp_canonical_creative_model__: ClassVar[bool] = True

    _serialize_canonical = model_serializer(mode="wrap")(_serialize_canonical_model)

    @classmethod
    def __pydantic_init_subclass__(cls, **kwargs: Any) -> None:
        """Remove inherited legacy creative identity from the declared fields.

        Each removed name is then bound as a plain class attribute holding
        ``None`` — which is the removal's own truth, and which the inherited
        validators need. A generated model can carry an injected validator that
        READS the removed field: ``list-creatives-response.json``'s
        ``_validate_format_reference_xor`` evaluates
        ``(self.format_id is None) == (self.format_kind is None)``. Inheritance
        is the point of this layer, so that validator now runs on the canonical
        model, and with the field merely deleted it raised ``AttributeError``.
        With the name reading ``None`` the XOR reduces to exactly the invariant
        the canonical model should hold — ``format_kind`` must be set — which is
        the same thing the canonical declaration states by making it required.

        Binding happens AFTER class creation, so pydantic never considers the
        name a field candidate; ``model_fields``, the JSON schema and the wire
        are all unaffected. Measured scope: one such validator, on one model.
        """

        removed = [name for name in cls.model_fields if is_legacy_creative_identity_key(name)]
        for name in removed:
            del cls.model_fields[name]
            cls.__annotations__.pop(name, None)
            setattr(cls, name, None)
        if removed:
            cls.model_rebuild(force=True)

    @model_validator(mode="before")
    @classmethod
    def _reject_legacy_creative_identity(cls, value: Any) -> Any:
        found = _legacy_creative_identity_path(
            value,
            allow_root_v1_ref=cls.__name__ == "Format",
            format_scope=cls.__name__ == "Format",
        )
        if found is not None:
            raise ValueError(
                f"{found} contains legacy creative identity; use an explicit Legacy* model"
            )
        return value

    def model_dump(self, **kwargs: Any) -> dict[str, Any]:
        kwargs.setdefault("serialize_as_any", False)
        stripped: dict[str, Any] = strip_legacy_creative_identity(
            super().model_dump(**kwargs),
            _format_scope=self.__class__.__name__ == "Format",
        )
        return stripped

    def model_dump_json(self, **kwargs: Any) -> str:
        kwargs.setdefault("serialize_as_any", False)
        raw = super().model_dump_json(**kwargs)
        clean = strip_legacy_creative_identity(
            json.loads(raw),
            _format_scope=self.__class__.__name__ == "Format",
        )
        indent = kwargs.get("indent")
        return json.dumps(
            clean,
            ensure_ascii=False,
            indent=indent,
            separators=None if indent is not None else (",", ":"),
        )

    @classmethod
    def model_json_schema(cls, *args: Any, **kwargs: Any) -> dict[str, Any]:
        return sanitize_canonical_schema(super().model_json_schema(*args, **kwargs))

    @classmethod
    def __get_pydantic_json_schema__(
        cls,
        core_schema: CoreSchema,
        handler: GetJsonSchemaHandler,
    ) -> dict[str, Any]:
        """Enforce the boundary for TypeAdapter and containing-model schemas."""

        schema = sanitize_canonical_schema(handler(core_schema))
        # TypeAdapter assembles shared definitions outside the model's returned
        # node. Mutate the active generator's definition registry as well so
        # unreachable generated legacy definitions cannot leak into the final
        # recursive schema document.
        generator = cast(_JsonSchemaHandlerWithGenerator, handler).generate_json_schema
        for key, definition in list(generator.definitions.items()):
            generator.definitions[key] = sanitize_canonical_schema(definition)
        return schema

Base class enforcing the primary canonical runtime boundary.

Every canonical model is a real subclass of the generated wire model it refines, so each one inherits the generated model's fields, its injected model_validators, and its envelope ancestry. Two concerns that used to be re-attached per class by create_model are therefore declared once, here, and inherited:

  • _serialize_canonical — the wrap serializer that strips legacy creative identity from nested and TypeAdapter serialization.
  • __pydantic_init_subclass__ — the one declared field removal. Legacy creative identity must be absent from a canonical model's declared fields, which is the single thing inheritance alone cannot express; the rule reads the same :func:is_legacy_creative_identity_key() predicate that governs the input validator, the schema sanitizer and the serializer, so there is one strip predicate for all four.

CanonicalBoundaryModel is listed LAST among a canonical model's bases. Pydantic merges model_config across bases left to right, so the right-most base wins; the generated wire model inherits :class:AdCPBaseModel's extra policy and would otherwise override this class's extra="allow" and start dropping caller-supplied extension keys.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Subclasses

Class variables

var model_config

Static methods

def model_json_schema(*args: Any, **kwargs: Any) ‑> dict[str, typing.Any]

Generates a JSON schema for a model class.

Args
-----=
by_alias
Whether to use attribute aliases or not.
ref_template
The reference template.
union_format

The format to use when combining schemas from unions together. Can be one of:

  • 'any_of': Use the anyOf keyword to combine schemas (the default).
  • 'primitive_type_array': Use the type keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive type (string, boolean, null, integer or number) or contains constraints/metadata, falls back to any_of.
schema_generator
To override the logic used to generate the JSON schema, as a subclass of GenerateJsonSchema with your desired modifications
mode
The mode in which to generate the schema.

Returns -----= The JSON schema for the given model class.

Inherited members

class CreateMediaBuyRequest (**data: Any)
Expand source code
class CreateMediaBuyRequest(_LegacyCreateMediaBuyRequest, CanonicalBoundaryModel):
    """Canonical create request; packages are canonical package requests."""

    packages: list[PackageRequest] | None = None

Canonical create request; packages are canonical package requests.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config
var packages : list[PackageRequest] | None

Inherited members

class CreateMediaBuyResponse1 (**data: Any)
Expand source code
class CreateMediaBuyResponse1(_LegacyCreateMediaBuyResponse1, CanonicalBoundaryModel):
    """Canonical create response preserving the 3.x legacy-status normalizer."""

    packages: list[Package]  # type: ignore[assignment]

    @model_validator(mode="before")
    @classmethod
    def _normalize_legacy_status(cls, data: Any) -> Any:
        if not isinstance(data, dict):
            return data
        raw_status = unwrap_enum_value(data.get("status"))
        media_buy_status = unwrap_enum_value(data.get("media_buy_status"))
        if raw_status is None or raw_status == "completed":
            return {**data, "status": "completed"}
        if media_buy_status is None and raw_status in MEDIA_BUY_LEGACY_STATUS_VALUES:
            return {**data, "media_buy_status": raw_status, "status": "completed"}
        if media_buy_status is not None and raw_status == media_buy_status:
            return {**data, "status": "completed"}
        return data

Canonical create response preserving the 3.x legacy-status normalizer.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config
var packages : list[Package]

Inherited members

class CreateMediaBuyResponse2 (**data: Any)
Expand source code
class CreateMediaBuyResponse2(_LegacyCreateMediaBuyResponse2, CanonicalBoundaryModel):
    """Canonical create-media-buy error arm."""

Canonical create-media-buy error arm.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config

Inherited members

class CreateMediaBuyResponse3 (**data: Any)
Expand source code
class CreateMediaBuyResponse3(_LegacyCreateMediaBuyResponse3, CanonicalBoundaryModel):
    """Canonical create-media-buy submitted arm."""

Canonical create-media-buy submitted arm.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config

Inherited members

class Creative (**data: Any)
Expand source code
class Creative(_CanonicalListedCreative, CanonicalBoundaryModel):
    """Canonical listed creative; the format kind is required, not optional.

    A listed creative is a row a seller RETURNS, so the kind is required but
    never confined: a kind a newer seller emits is retained as-is, which is
    what ``core/canonical-format-kind.json`` requires of a consumer.
    """

    if TYPE_CHECKING:  # the removed field, hidden from the constructor too
        format_id: _RemovedFormatId = Field(default=None, init=False)

    format_kind: str

Canonical listed creative; the format kind is required, not optional.

A listed creative is a row a seller RETURNS, so the kind is required but never confined: a kind a newer seller emits is retained as-is, which is what core/canonical-format-kind.json requires of a consumer.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var format_id : FormatReferenceStructuredObject | None
var format_kind : str
var model_config

Inherited members

class CreativeAsset (**data: Any)
Expand source code
class CreativeAsset(_CanonicalCreativeWire, CanonicalBoundaryModel):
    """Canonical creative asset; the format kind is required, not optional.

    The kind is narrowed to required and nothing else: it stays ``str`` and the
    model refuses no value. A buyer SENDS a creative asset, and the
    producer-side "sellers MUST NOT mint ad-hoc kinds" rule is the sender's
    obligation, not something a pinned library can tell from a kind defined
    after its pin — :func:`is_canonical_format_kind` is how a caller meets it.
    """

    if TYPE_CHECKING:  # the removed field, hidden from the constructor too
        format_id: _RemovedFormatId = Field(default=None, init=False)

    format_kind: str

Canonical creative asset; the format kind is required, not optional.

The kind is narrowed to required and nothing else: it stays str and the model refuses no value. A buyer SENDS a creative asset, and the producer-side "sellers MUST NOT mint ad-hoc kinds" rule is the sender's obligation, not something a pinned library can tell from a kind defined after its pin — :func:is_canonical_format_kind() is how a caller meets it.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var format_id : FormatReferenceStructuredObject | None
var format_kind : str
var model_config

Instance variables

var placement_ids : list[str] | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.

Inherited members

class CreativeFilters (**data: Any)
Expand source code
class CreativeFilters(_LegacyCreativeFilters, CanonicalBoundaryModel):
    """Canonical creative filters; legacy identity selection is unavailable."""

    if TYPE_CHECKING:  # the removed field, hidden from the constructor too
        format_ids: _RemovedFormatIds = Field(default=None, init=False)

Canonical creative filters; legacy identity selection is unavailable.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var format_ids : list[FormatReferenceStructuredObject] | None
var model_config

Inherited members

class CreativeManifest (**data: Any)
Expand source code
class CreativeManifest(_CanonicalCreativeManifestWire, CanonicalBoundaryModel):
    """Canonical manifest accepting the SDK's public standalone asset models.

    The 3.2 aggregate asset-union schema currently generates structurally
    duplicate Pydantic classes. Convert public ``ImageContent``/``UrlContent``
    (and peers) back to their wire dictionaries before the aggregate union
    validates them. This keeps the public constructors composable without
    relaxing the on-wire discriminator checks.
    """

    if TYPE_CHECKING:  # the removed field, hidden from the constructor too
        format_id: _RemovedFormatId = Field(default=None, init=False)

    @model_validator(mode="before")
    @classmethod
    def _normalize_standalone_assets(cls, data: Any) -> Any:
        if not isinstance(data, dict) or not isinstance(data.get("assets"), dict):
            return data

        def wire_value(value: Any) -> Any:
            if isinstance(value, AdCPBaseModel):
                return value.model_dump(mode="json", exclude_none=True)
            if isinstance(value, list):
                return [wire_value(item) for item in value]
            return value

        return {
            **data,
            "assets": {key: wire_value(value) for key, value in data["assets"].items()},
        }

Canonical manifest accepting the SDK's public standalone asset models.

The 3.2 aggregate asset-union schema currently generates structurally duplicate Pydantic classes. Convert public ImageContent/UrlContent (and peers) back to their wire dictionaries before the aggregate union validates them. This keeps the public constructors composable without relaxing the on-wire discriminator checks.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config

Inherited members

class CreativeVariant (**data: Any)
Expand source code
class CreativeVariant(_LegacyCreativeVariant, CanonicalBoundaryModel):
    """Canonical creative variant whose manifest is the canonical manifest."""

    manifest: CreativeManifest | None = None

Canonical creative variant whose manifest is the canonical manifest.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var manifest : CreativeManifest | None
var model_config

Inherited members

class DeliveryCreative (**data: Any)
Expand source code
class DeliveryCreative(_LegacyDeliveryCreative, CanonicalBoundaryModel):
    """Canonical served creative; variants are the read-back delivery rows."""

    if TYPE_CHECKING:  # the removed field, hidden from the constructor too
        format_id: _RemovedFormatId = Field(default=None, init=False)

    variants: list[_DeliveryCreativeVariant] = _inherit(  # type: ignore[assignment]
        _LegacyDeliveryCreative, "variants"
    )

Canonical served creative; variants are the read-back delivery rows.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config
var variants : list[adcp.types.canonical_creative._DeliveryCreativeVariant]

Inherited members

class Format (**data: Any)
Expand source code
class Format(CanonicalBoundaryModel):
    """Canonical format declaration exposed as ``adcp.Format``."""

    format_option_id: str | None = Field(
        default=None,
        description="Stable option identifier within the product or publisher namespace.",
    )
    publisher_domain: str | None = Field(
        default=None,
        pattern=r"^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$",
    )
    display_name: str | None = None
    applies_to_channels: list[MediaChannel] | None = None
    seller_preference: SellerPreference | None = None
    canonical_formats_only: bool | None = None
    experimental: bool | None = None
    format_shape: str | None = None
    format_schema: PlatformExtensionReference | None = None
    format_kind: str
    params: dict[str, Any]

    _legacy_format_refs: list[LegacyFormatId] = PrivateAttr(default_factory=list)

    @model_validator(mode="before")
    @classmethod
    def _reject_legacy_conflicts_and_credentials(cls, data: Any) -> Any:
        if not isinstance(data, dict):
            return data
        if data.get("canonical_formats_only") is True and data.get("v1_format_ref"):
            raise ValueError(
                "canonical_formats_only=True is mutually exclusive with legacy v1_format_ref"
            )
        for bag_name, bag in (
            ("params", data.get("params")),
            (
                "extras",
                {
                    key: value
                    for key, value in data.items()
                    if key not in cls.model_fields and key != "v1_format_ref"
                },
            ),
        ):
            found = _walk_for_credential_keys(bag, path=bag_name)
            if found is not None:
                raise ValueError(
                    f"{found!r} matches a credential-shaped key suffix and cannot "
                    "be stored in a canonical format declaration"
                )
        return data

    def __init__(self, **data: Any) -> None:
        refs = data.get("v1_format_ref")
        if "capability_id" in data and "format_option_id" not in data:
            data["format_option_id"] = data.pop("capability_id")
        super().__init__(**data)
        if self.__pydantic_extra__ is not None:
            self.__pydantic_extra__.pop("v1_format_ref", None)
        if refs:
            self._legacy_format_refs = [
                LegacyFormatId.model_validate(copy.deepcopy(ref)) for ref in refs
            ]

    @property
    def legacy_format_refs(self) -> tuple[LegacyFormatId, ...]:
        """Original tuples retained only for an explicit compatibility adapter."""

        return tuple(copy.deepcopy(ref) for ref in self._legacy_format_refs)

    def params_as(self, canonical_type: type[_CanonicalParamsT]) -> _CanonicalParamsT:
        """Validate the open parameter bag against a typed canonical model."""

        return canonical_type.model_validate(self.params)

    @model_validator(mode="after")
    def _validate_custom_shape(self) -> Format:
        if self.format_kind == CanonicalFormatKind.custom.value:
            if not self.format_shape:
                raise ValueError("custom formats require format_shape")
            if self.format_schema is None:
                raise ValueError("custom formats require format_schema")
        elif self.format_shape is not None or self.format_schema is not None:
            raise ValueError("format_shape and format_schema are only valid for custom formats")
        return self

Canonical format declaration exposed as Format.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var applies_to_channels : list[MediaChannel] | None
var canonical_formats_only : bool | None
var display_name : str | None
var experimental : bool | None
var format_kind : str
var format_option_id : str | None
var format_schema : PlatformExtensionReference | None
var format_shape : str | None
var model_config
var params : dict[str, typing.Any]
var publisher_domain : str | None
var seller_preference : SellerPreference | None

Instance variables

prop legacy_format_refs : tuple[LegacyFormatId, ...]
Expand source code
@property
def legacy_format_refs(self) -> tuple[LegacyFormatId, ...]:
    """Original tuples retained only for an explicit compatibility adapter."""

    return tuple(copy.deepcopy(ref) for ref in self._legacy_format_refs)

Original tuples retained only for an explicit compatibility adapter.

Methods

def model_post_init(self: BaseModel, context: Any, /) ‑> None
Expand source code
def init_private_attributes(self: BaseModel, context: Any, /) -> None:
    """This function is meant to behave like a BaseModel method to initialize private attributes.

    It takes context as an argument since that's what pydantic-core passes when calling it.

    Args:
        self: The BaseModel instance.
        context: The context.
    """
    if getattr(self, '__pydantic_private__', None) is None:
        pydantic_private = {}
        for name, private_attr in self.__private_attributes__.items():
            # Avoid needlessly creating a new dict for the validated data:
            if private_attr.default_factory_takes_validated_data:
                default = private_attr.get_default(
                    call_default_factory=True, validated_data={**self.__dict__, **pydantic_private}
                )
            else:
                default = private_attr.get_default(call_default_factory=True)
            if default is not PydanticUndefined:
                pydantic_private[name] = default
        object_setattr(self, '__pydantic_private__', pydantic_private)

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that's what pydantic-core passes when calling it.

Args
-----=
self
The BaseModel instance.
context
The context.
def params_as(self, canonical_type: type[_CanonicalParamsT]) ‑> ~_CanonicalParamsT
Expand source code
def params_as(self, canonical_type: type[_CanonicalParamsT]) -> _CanonicalParamsT:
    """Validate the open parameter bag against a typed canonical model."""

    return canonical_type.model_validate(self.params)

Validate the open parameter bag against a typed canonical model.

class ProductFormatDeclaration (**data: Any)
Expand source code
class Format(CanonicalBoundaryModel):
    """Canonical format declaration exposed as ``adcp.Format``."""

    format_option_id: str | None = Field(
        default=None,
        description="Stable option identifier within the product or publisher namespace.",
    )
    publisher_domain: str | None = Field(
        default=None,
        pattern=r"^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$",
    )
    display_name: str | None = None
    applies_to_channels: list[MediaChannel] | None = None
    seller_preference: SellerPreference | None = None
    canonical_formats_only: bool | None = None
    experimental: bool | None = None
    format_shape: str | None = None
    format_schema: PlatformExtensionReference | None = None
    format_kind: str
    params: dict[str, Any]

    _legacy_format_refs: list[LegacyFormatId] = PrivateAttr(default_factory=list)

    @model_validator(mode="before")
    @classmethod
    def _reject_legacy_conflicts_and_credentials(cls, data: Any) -> Any:
        if not isinstance(data, dict):
            return data
        if data.get("canonical_formats_only") is True and data.get("v1_format_ref"):
            raise ValueError(
                "canonical_formats_only=True is mutually exclusive with legacy v1_format_ref"
            )
        for bag_name, bag in (
            ("params", data.get("params")),
            (
                "extras",
                {
                    key: value
                    for key, value in data.items()
                    if key not in cls.model_fields and key != "v1_format_ref"
                },
            ),
        ):
            found = _walk_for_credential_keys(bag, path=bag_name)
            if found is not None:
                raise ValueError(
                    f"{found!r} matches a credential-shaped key suffix and cannot "
                    "be stored in a canonical format declaration"
                )
        return data

    def __init__(self, **data: Any) -> None:
        refs = data.get("v1_format_ref")
        if "capability_id" in data and "format_option_id" not in data:
            data["format_option_id"] = data.pop("capability_id")
        super().__init__(**data)
        if self.__pydantic_extra__ is not None:
            self.__pydantic_extra__.pop("v1_format_ref", None)
        if refs:
            self._legacy_format_refs = [
                LegacyFormatId.model_validate(copy.deepcopy(ref)) for ref in refs
            ]

    @property
    def legacy_format_refs(self) -> tuple[LegacyFormatId, ...]:
        """Original tuples retained only for an explicit compatibility adapter."""

        return tuple(copy.deepcopy(ref) for ref in self._legacy_format_refs)

    def params_as(self, canonical_type: type[_CanonicalParamsT]) -> _CanonicalParamsT:
        """Validate the open parameter bag against a typed canonical model."""

        return canonical_type.model_validate(self.params)

    @model_validator(mode="after")
    def _validate_custom_shape(self) -> Format:
        if self.format_kind == CanonicalFormatKind.custom.value:
            if not self.format_shape:
                raise ValueError("custom formats require format_shape")
            if self.format_schema is None:
                raise ValueError("custom formats require format_schema")
        elif self.format_shape is not None or self.format_schema is not None:
            raise ValueError("format_shape and format_schema are only valid for custom formats")
        return self

Canonical format declaration exposed as Format.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var applies_to_channels : list[MediaChannel] | None
var canonical_formats_only : bool | None
var display_name : str | None
var experimental : bool | None
var format_kind : str
var format_option_id : str | None
var format_schema : PlatformExtensionReference | None
var format_shape : str | None
var model_config
var params : dict[str, typing.Any]
var publisher_domain : str | None
var seller_preference : SellerPreference | None

Instance variables

prop legacy_format_refs : tuple[LegacyFormatId, ...]
Expand source code
@property
def legacy_format_refs(self) -> tuple[LegacyFormatId, ...]:
    """Original tuples retained only for an explicit compatibility adapter."""

    return tuple(copy.deepcopy(ref) for ref in self._legacy_format_refs)

Original tuples retained only for an explicit compatibility adapter.

Methods

def model_post_init(self: BaseModel, context: Any, /) ‑> None
Expand source code
def init_private_attributes(self: BaseModel, context: Any, /) -> None:
    """This function is meant to behave like a BaseModel method to initialize private attributes.

    It takes context as an argument since that's what pydantic-core passes when calling it.

    Args:
        self: The BaseModel instance.
        context: The context.
    """
    if getattr(self, '__pydantic_private__', None) is None:
        pydantic_private = {}
        for name, private_attr in self.__private_attributes__.items():
            # Avoid needlessly creating a new dict for the validated data:
            if private_attr.default_factory_takes_validated_data:
                default = private_attr.get_default(
                    call_default_factory=True, validated_data={**self.__dict__, **pydantic_private}
                )
            else:
                default = private_attr.get_default(call_default_factory=True)
            if default is not PydanticUndefined:
                pydantic_private[name] = default
        object_setattr(self, '__pydantic_private__', pydantic_private)

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that's what pydantic-core passes when calling it.

Args
-----=
self
The BaseModel instance.
context
The context.
def params_as(self, canonical_type: type[_CanonicalParamsT]) ‑> ~_CanonicalParamsT
Expand source code
def params_as(self, canonical_type: type[_CanonicalParamsT]) -> _CanonicalParamsT:
    """Validate the open parameter bag against a typed canonical model."""

    return canonical_type.model_validate(self.params)

Validate the open parameter bag against a typed canonical model.

Inherited members

class GetCreativeDeliveryResponse (**data: Any)
Expand source code
class GetCreativeDeliveryResponse(_LegacyGetCreativeDeliveryResponse, CanonicalBoundaryModel):
    """Canonical creative delivery response; rows are the read-back delivery creatives."""

    creatives: Sequence[DeliveryCreative]

Canonical creative delivery response; rows are the read-back delivery creatives.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var creatives : Sequence[DeliveryCreative]
var model_config

Inherited members

class GetMediaBuyDeliveryResponse (**data: Any)
Expand source code
class GetMediaBuyDeliveryResponse(_LegacyGetMediaBuyDeliveryResponse, CanonicalBoundaryModel):
    """Canonical media-buy delivery response."""

Canonical media-buy delivery response.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config

Inherited members

class GetMediaBuysResponse (**data: Any)
Expand source code
class GetMediaBuysResponse(_LegacyGetMediaBuysResponse, CanonicalBoundaryModel):
    """Canonical media-buy listing; rows are canonical media buys."""

    media_buys: Sequence[MediaBuy]

Canonical media-buy listing; rows are canonical media buys.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var media_buys : Sequence[MediaBuy]
var model_config

Inherited members

class GetProductsRequest (**data: Any)
Expand source code
class GetProductsRequest(_LegacyGetProductsRequest, CanonicalBoundaryModel):
    """Canonical discovery request with legacy response-field selection rejected."""

    filters: ProductFilters | None = None

    @field_validator("fields")
    @classmethod
    def _reject_legacy_fields(cls, value: Any) -> Any:
        if value and any(
            is_legacy_creative_identity_key(getattr(item, "value", item)) for item in value
        ):
            raise ValueError(
                "format_id and format_ids are unavailable on the canonical get_products API"
            )
        return value

Canonical discovery request with legacy response-field selection rejected.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var filters : ProductFilters | None
var model_config

Inherited members

class GetProductsResponse (**data: Any)
Expand source code
class GetProductsResponse(_LegacyGetProductsResponse, CanonicalBoundaryModel):
    """Canonical discovery response; products are canonical products."""

    products: list[Product] | None = None  # type: ignore[assignment]

Canonical discovery response; products are canonical products.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config
var products : list[Product] | None

Inherited members

class ListCreativesRequest (**data: Any)
Expand source code
class ListCreativesRequest(_LegacyListCreativesRequest, CanonicalBoundaryModel):
    """Canonical creative read request with legacy field selection rejected."""

    filters: CreativeFilters | None = None

    @field_validator("fields")
    @classmethod
    def _reject_legacy_fields(cls, value: Any) -> Any:
        if value and any(
            is_legacy_creative_identity_key(getattr(item, "value", item)) for item in value
        ):
            raise ValueError(
                "format_id and format_ids are unavailable on the canonical list_creatives API"
            )
        return value

Canonical creative read request with legacy field selection rejected.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var filters : CreativeFilters | None
var model_config

Inherited members

class ListCreativesResponse (**data: Any)
Expand source code
class ListCreativesResponse(_LegacyListCreativesResponse, CanonicalBoundaryModel):
    """Canonical creative listing; rows are canonical listed creatives."""

    creatives: list[Creative]

Canonical creative listing; rows are canonical listed creatives.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var creatives : list[Creative]
var model_config

Inherited members

class MediaBuy (**data: Any)
Expand source code
class MediaBuy(_LegacyMediaBuy, CanonicalBoundaryModel):
    """Canonical media buy; packages are canonical package rows."""

    packages: Sequence[MediaBuyPackage]

Canonical media buy; packages are canonical package rows.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config
var packages : Sequence[MediaBuyPackage]

Inherited members

class MediaBuyPackage (**data: Any)
Expand source code
class MediaBuyPackage(_LegacyMediaBuyPackage, CanonicalBoundaryModel):
    """Canonical media-buy package row; legacy format identity is absent."""

    if TYPE_CHECKING:  # the removed fields, hidden from the constructor too
        format_ids: _RemovedFormatIds = Field(default=None, init=False)
        format_ids_pending: _RemovedFormatIds = Field(default=None, init=False)
        format_ids_to_provide: _RemovedFormatIds = Field(default=None, init=False)

Canonical media-buy package row; legacy format identity is absent.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var format_ids : list[FormatReferenceStructuredObject] | None
var format_ids_pending : list[FormatReferenceStructuredObject] | None
var format_ids_to_provide : list[FormatReferenceStructuredObject] | None
var model_config

Instance variables

var bid_price : float | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.

Inherited members

class Package (**data: Any)
Expand source code
class Package(_LegacyPackage, CanonicalBoundaryModel):
    """Canonical package; legacy format identity is absent."""

    if TYPE_CHECKING:  # the removed fields, hidden from the constructor too
        format_ids: _RemovedFormatIds = Field(default=None, init=False)
        format_ids_pending: _RemovedFormatIds = Field(default=None, init=False)
        format_ids_to_provide: _RemovedFormatIds = Field(default=None, init=False)

Canonical package; legacy format identity is absent.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var format_ids : list[FormatReferenceStructuredObject] | None
var format_ids_pending : list[FormatReferenceStructuredObject] | None
var format_ids_to_provide : list[FormatReferenceStructuredObject] | None
var model_config

Instance variables

var bid_price : float | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.

Inherited members

class PackageRequest (**data: Any)
Expand source code
class PackageRequest(_LegacyPackageRequest, CanonicalBoundaryModel):
    """Canonical package request preserving beta.3 selector constraints."""

    if TYPE_CHECKING:  # the removed field, hidden from the constructor too
        format_ids: _RemovedFormatIds = Field(default=None, init=False)

    creatives: list[CreativeAsset] | None = Field(default=None, min_length=1)

    @model_validator(mode="after")
    def _validate_format_params(self) -> PackageRequest:
        if self.params is not None and self.format_kind is None:
            raise ValueError("params requires format_kind")
        if self.params is not None and self.format_kind == "image":
            if ("width" in self.params) != ("height" in self.params):
                raise ValueError("image params width and height must co-occur")
        return self

Canonical package request preserving beta.3 selector constraints.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var creatives : list[CreativeAsset] | None
var model_config
var targeting_overlay : TargetingOverlayInput | TargetingOverlay | None

Inherited members

class PackageUpdate (**data: Any)
Expand source code
class PackageUpdate(_LegacyPackageUpdate, CanonicalBoundaryModel):
    """Canonical package update; creatives are canonical assets."""

    creatives: list[CreativeAsset] | None = Field(default=None, min_length=1)  # type: ignore[assignment]

Canonical package update; creatives are canonical assets.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var creatives : list[CreativeAsset] | None
var model_config
var targeting_overlay : TargetingOverlayInput | TargetingOverlay | None

Inherited members

class Placement (**data: Any)
Expand source code
class Placement(_LegacyPlacement, CanonicalBoundaryModel):
    """Canonical placement; ``format_options`` are canonical declarations."""

    if TYPE_CHECKING:  # the removed field, hidden from the constructor too
        format_ids: _RemovedFormatIdSequence = Field(default=None, init=False)

    format_options: list[Format] | None = Field(default=None, min_length=1)  # type: ignore[assignment]

Canonical placement; format_options are canonical declarations.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var format_ids : collections.abc.Sequence[FormatReferenceStructuredObject] | None
var format_options : list[Format] | None
var model_config

Inherited members

class Product (**data: Any)
Expand source code
class Product(_LegacyProduct, CanonicalBoundaryModel):
    """Canonical product; formats, placements and pricing are canonical."""

    if TYPE_CHECKING:  # the removed field, hidden from the constructor too
        format_ids: _RemovedFormatIds = Field(default=None, init=False)

    format_options: list[Format] = Field(  # type: ignore[assignment]
        min_length=1, description="Canonical creative formats accepted by this product."
    )
    placements: list[Placement] | None = Field(default=None, min_length=1)  # type: ignore[assignment]
    pricing_options: list[CanonicalPricingOption] = Field(min_length=1)

Canonical product; formats, placements and pricing are canonical.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var format_ids : list[FormatReferenceStructuredObject] | None
var format_options : list[Format]
var model_config
var placements : list[Placement] | None
var pricing_options : list[CpmPricingOption | VcpmPricingOption | CpcPricingOption | CpcvPricingOption | CpvPricingOption | CppPricingOption | CpaPricingOption | RevenueSharePricingOption | FlatRatePricingOption | TimeBasedPricingOption]

Instance variables

var data_provider_signals : list[DataProviderSignalSelector1 | DataProviderSignalSelector2 | DataProviderSignalSelector3] | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.
var outcome_measurement : OutcomeMeasurement | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.

Inherited members

class ProductFilters (**data: Any)
Expand source code
class ProductFilters(_LegacyProductFilters, CanonicalBoundaryModel):
    """Canonical product filters; legacy identity selection is unavailable."""

    if TYPE_CHECKING:  # the removed field, hidden from the constructor too
        format_ids: _RemovedFormatIds = Field(default=None, init=False)

Canonical product filters; legacy identity selection is unavailable.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var format_ids : list[FormatReferenceStructuredObject] | None
var model_config

Instance variables

var countries : list[Country] | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.
var geo_proximity : list[GeoProximityItem] | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.
var keywords : list[Keyword] | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.
var metros : list[Metro] | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.
var postal_areas : list[PostalArea] | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.
var regions : list[Region] | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.
var required_axe_integrations : list[pydantic.networks.AnyUrl] | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.
var required_geo_targeting : list[RequiredGeoTargetingItem] | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.
var signal_targeting : list[SignalTargetingItem5 | SignalTargetingItem6 | SignalTargetingItem7] | None
Expand source code
def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
    if obj is None:
        if self.wrapped_property is not None:
            return self.wrapped_property.__get__(None, obj_type)
        raise AttributeError(self.field_name)

    warnings.warn(self.msg, DeprecationWarning, stacklevel=2)

    if self.wrapped_property is not None:
        return self.wrapped_property.__get__(obj, obj_type)
    return obj.__dict__[self.field_name]

Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.

Attributes
-----=
msg
The deprecation message to be emitted.
wrapped_property
The property instance if the deprecated field is a computed field, or None.
field_name
The name of the field being deprecated.

Inherited members

class SyncCreativesRequest (**data: Any)
Expand source code
class SyncCreativesRequest(_LegacySyncCreativesRequest, CanonicalBoundaryModel):
    """Canonical creative sync request; creatives are canonical assets."""

    creatives: list[CreativeAsset] = Field(min_length=1)  # type: ignore[assignment]

Canonical creative sync request; creatives are canonical assets.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var creatives : list[CreativeAsset]
var model_config

Inherited members

class UpdateMediaBuyRequest (**data: Any)
Expand source code
class UpdateMediaBuyRequest(_LegacyUpdateMediaBuyRequest, CanonicalBoundaryModel):
    """Canonical update request; both package lists are canonical."""

    packages: list[PackageUpdate] | None = None
    new_packages: list[PackageRequest] | None = Field(  # type: ignore[assignment]
        default=None, min_length=1
    )

Canonical update request; both package lists are canonical.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config
var new_packages : list[PackageRequest] | None
var packages : list[PackageUpdate] | None

Inherited members

class UpdateMediaBuyResponse1 (**data: Any)
Expand source code
class UpdateMediaBuyResponse1(_LegacyUpdateMediaBuyResponse1, CanonicalBoundaryModel):
    """Canonical update response preserving the 3.x legacy-status normalizer."""

    affected_packages: Sequence[Package] | None = None

    @model_validator(mode="before")
    @classmethod
    def _normalize_legacy_status(cls, data: Any) -> Any:
        if not isinstance(data, dict):
            return data
        raw_status = unwrap_enum_value(data.get("status"))
        media_buy_status = unwrap_enum_value(data.get("media_buy_status"))
        if raw_status is None or raw_status == "completed":
            return {**data, "status": "completed"}
        if media_buy_status is None and raw_status in MEDIA_BUY_LEGACY_STATUS_VALUES:
            return {**data, "media_buy_status": raw_status, "status": "completed"}
        if media_buy_status is not None and raw_status == media_buy_status:
            return {**data, "status": "completed"}
        return data

Canonical update response preserving the 3.x legacy-status normalizer.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var affected_packages : collections.abc.Sequence[Package] | None
var model_config

Inherited members

class UpdateMediaBuyResponse2 (**data: Any)
Expand source code
class UpdateMediaBuyResponse2(_LegacyUpdateMediaBuyResponse2, CanonicalBoundaryModel):
    """Canonical update-media-buy error arm."""

Canonical update-media-buy error arm.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config

Inherited members

class UpdateMediaBuyResponse3 (**data: Any)
Expand source code
class UpdateMediaBuyResponse3(_LegacyUpdateMediaBuyResponse3, CanonicalBoundaryModel):
    """Canonical update-media-buy submitted arm."""

Canonical update-media-buy submitted arm.

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

Ancestors

Class variables

var model_config

Inherited members