Skip to content

sgn.validator

Validators for simplifying element validation.

PadConstraint dataclass

Declarative pad topology constraint enforced by a validator.

When a validator from this module is used as a decorator, the constraint it enforces is attached to the decorated method as a pad_constraint attribute, so that tools (e.g. sgn-inspect) can introspect the pad topology an element expects without instantiating it. Stacked validator decorators merge their constraints.

Parameters:

Name Type Description Default
sink_pads int | None

int, optional, the exact number of sink pads required, or None if unconstrained

None
source_pads int | None

int, optional, the exact number of source pads required, or None if unconstrained

None
pad_names_match bool

bool, whether source and sink pad names are required to match

False
Source code in src/sgn/validator.py
@dataclass(frozen=True)
class PadConstraint:
    """Declarative pad topology constraint enforced by a validator.

    When a validator from this module is used as a decorator, the constraint
    it enforces is attached to the decorated method as a ``pad_constraint``
    attribute, so that tools (e.g. sgn-inspect) can introspect the pad
    topology an element expects without instantiating it. Stacked validator
    decorators merge their constraints.

    Args:
        sink_pads:
            int, optional, the exact number of sink pads required, or None
            if unconstrained
        source_pads:
            int, optional, the exact number of source pads required, or None
            if unconstrained
        pad_names_match:
            bool, whether source and sink pad names are required to match
    """

    sink_pads: int | None = None
    source_pads: int | None = None
    pad_names_match: bool = False

    def merge(self, other: PadConstraint) -> PadConstraint:
        """Combine this constraint with another, with the other's counts
        taking precedence where both are set.

        Args:
            other: The constraint to merge on top of this one

        Returns:
            The combined constraint
        """
        return PadConstraint(
            sink_pads=(
                other.sink_pads if other.sink_pads is not None else self.sink_pads
            ),
            source_pads=(
                other.source_pads if other.source_pads is not None else self.source_pads
            ),
            pad_names_match=self.pad_names_match or other.pad_names_match,
        )

merge(other)

Combine this constraint with another, with the other's counts taking precedence where both are set.

Parameters:

Name Type Description Default
other PadConstraint

The constraint to merge on top of this one

required

Returns:

Type Description
PadConstraint

The combined constraint

Source code in src/sgn/validator.py
def merge(self, other: PadConstraint) -> PadConstraint:
    """Combine this constraint with another, with the other's counts
    taking precedence where both are set.

    Args:
        other: The constraint to merge on top of this one

    Returns:
        The combined constraint
    """
    return PadConstraint(
        sink_pads=(
            other.sink_pads if other.sink_pads is not None else self.sink_pads
        ),
        source_pads=(
            other.source_pads if other.source_pads is not None else self.source_pads
        ),
        pad_names_match=self.pad_names_match or other.pad_names_match,
    )

many_to_one(arg)

many_to_one(arg: TMethod) -> TMethod
many_to_one(arg: T) -> None

Validator for many-to-one transforms.

Can be used as a decorator or called directly::

# As a decorator
class MyTransform(TransformElement):
    @validator.many_to_one
    def validate(self) -> None:
        pass

# Called directly
class MyTransform(TransformElement):
    def __post_init__(self):
        super().__post_init__()
        validator.many_to_one(self)
Source code in src/sgn/validator.py
def many_to_one(arg: TMethod | T) -> TMethod | None:
    """Validator for many-to-one transforms.

    Can be used as a decorator or called directly::

        # As a decorator
        class MyTransform(TransformElement):
            @validator.many_to_one
            def validate(self) -> None:
                pass

        # Called directly
        class MyTransform(TransformElement):
            def __post_init__(self):
                super().__post_init__()
                validator.many_to_one(self)
    """

    def validate(self: T) -> None:
        assert len(self.source_pads) == 1, (
            f"{self.name} requires exactly one source pad, "
            f"got {len(self.source_pads)}"
        )

    return _make_validator(validate, PadConstraint(source_pads=1))(arg)

num_pads(sink_pads=None, source_pads=None)

Validator for specific number of sink and source pads.

Can be used as a decorator or called directly::

# As a decorator
class MyTransform(TransformElement):
    @validator.num_pads(sink_pads=2, source_pads=1)
    def validate(self) -> None:
        # Additional custom validation
        pass

# Called directly
class MyTransform(TransformElement):
    def __post_init__(self):
        super().__post_init__()
        validator.num_pads(sink_pads=2, source_pads=1)(self)
Source code in src/sgn/validator.py
def num_pads(
    sink_pads: int | None = None, source_pads: int | None = None
) -> Callable[[TMethod], TMethod]:
    """Validator for specific number of sink and source pads.

    Can be used as a decorator or called directly::

        # As a decorator
        class MyTransform(TransformElement):
            @validator.num_pads(sink_pads=2, source_pads=1)
            def validate(self) -> None:
                # Additional custom validation
                pass

        # Called directly
        class MyTransform(TransformElement):
            def __post_init__(self):
                super().__post_init__()
                validator.num_pads(sink_pads=2, source_pads=1)(self)
    """

    def validate(self: T) -> None:
        if sink_pads is not None:
            assert len(self.sink_pads) == sink_pads, (
                f"{self.name} requires exactly {sink_pads} sink pad(s), "
                f"got {len(self.sink_pads)}"
            )
        if source_pads is not None:
            assert len(self.source_pads) == source_pads, (
                f"{self.name} requires exactly {source_pads} source pad(s), "
                f"got {len(self.source_pads)}"
            )

    return _make_validator(  # type: ignore[return-value]
        validate, PadConstraint(sink_pads=sink_pads, source_pads=source_pads)
    )

one_to_many(arg)

one_to_many(arg: TMethod) -> TMethod
one_to_many(arg: T) -> None

Validator for one-to-many transforms.

Can be used as a decorator or called directly::

# As a decorator
class MyTransform(TransformElement):
    @validator.one_to_many
    def validate(self) -> None:
        pass

# Called directly
class MyTransform(TransformElement):
    def __post_init__(self):
        super().__post_init__()
        validator.one_to_many(self)
Source code in src/sgn/validator.py
def one_to_many(arg: TMethod | T) -> TMethod | None:
    """Validator for one-to-many transforms.

    Can be used as a decorator or called directly::

        # As a decorator
        class MyTransform(TransformElement):
            @validator.one_to_many
            def validate(self) -> None:
                pass

        # Called directly
        class MyTransform(TransformElement):
            def __post_init__(self):
                super().__post_init__()
                validator.one_to_many(self)
    """

    def validate(self: T) -> None:
        assert len(self.sink_pads) == 1, (
            f"{self.name} requires exactly one sink pad, " f"got {len(self.sink_pads)}"
        )

    return _make_validator(validate, PadConstraint(sink_pads=1))(arg)

one_to_one(arg)

one_to_one(arg: TMethod) -> TMethod
one_to_one(arg: T) -> None

Validator for one-to-one transforms.

Can be used as a decorator or called directly::

# As a decorator
class MyTransform(TransformElement):
    @validator.one_to_one
    def validate(self) -> None:
        pass

# Called directly
class MyTransform(TransformElement):
    def __post_init__(self):
        super().__post_init__()
        validator.one_to_one(self)
Source code in src/sgn/validator.py
def one_to_one(arg: TMethod | T) -> TMethod | None:
    """Validator for one-to-one transforms.

    Can be used as a decorator or called directly::

        # As a decorator
        class MyTransform(TransformElement):
            @validator.one_to_one
            def validate(self) -> None:
                pass

        # Called directly
        class MyTransform(TransformElement):
            def __post_init__(self):
                super().__post_init__()
                validator.one_to_one(self)
    """

    def validate(self: T) -> None:
        assert len(self.sink_pads) == 1 and len(self.source_pads) == 1, (
            f"{self.name} requires exactly one sink and source pad, "
            f"got {len(self.sink_pads)} sink pads and "
            f"{len(self.source_pads)} source pads"
        )

    return _make_validator(validate, PadConstraint(sink_pads=1, source_pads=1))(arg)

pad_names_match(arg)

pad_names_match(arg: TMethod) -> TMethod
pad_names_match(arg: T) -> None

Validator that source and sink pad names match.

Can be used as a decorator or called directly::

# As a decorator
class MyTransform(TransformElement):
    @validator.pad_names_match
    def validate(self) -> None:
        pass

# Called directly
class MyTransform(TransformElement):
    def __post_init__(self):
        super().__post_init__()
        validator.pad_names_match(self)
Source code in src/sgn/validator.py
def pad_names_match(arg: TMethod | T) -> TMethod | None:
    """Validator that source and sink pad names match.

    Can be used as a decorator or called directly::

        # As a decorator
        class MyTransform(TransformElement):
            @validator.pad_names_match
            def validate(self) -> None:
                pass

        # Called directly
        class MyTransform(TransformElement):
            def __post_init__(self):
                super().__post_init__()
                validator.pad_names_match(self)
    """

    def validate(self: T) -> None:
        # set() is appropriate here since pad names are unique per element
        assert set(self.source_pad_names) == set(self.sink_pad_names), (
            f"{self.name} requires source and sink pad names to match. "
            f"Source: {self.source_pad_names}, Sink: {self.sink_pad_names}"
        )

    return _make_validator(validate, PadConstraint(pad_names_match=True))(arg)

single_pad(arg)

single_pad(arg: SMethod) -> SMethod
single_pad(arg: S) -> None

Validator for single-pad sinks.

Can be used as a decorator or called directly::

# As a decorator
class MySink(SinkElement):
    @validator.single_pad
    def validate(self) -> None:
        pass

# Called directly
class MySink(SinkElement):
    def __post_init__(self):
        super().__post_init__()
        validator.single_pad(self)
Source code in src/sgn/validator.py
def single_pad(arg: SMethod | S) -> SMethod | None:
    """Validator for single-pad sinks.

    Can be used as a decorator or called directly::

        # As a decorator
        class MySink(SinkElement):
            @validator.single_pad
            def validate(self) -> None:
                pass

        # Called directly
        class MySink(SinkElement):
            def __post_init__(self):
                super().__post_init__()
                validator.single_pad(self)
    """

    def validate(self: S) -> None:
        assert len(self.sink_pads) == 1, (
            f"{self.name} requires exactly one sink pad, " f"got {len(self.sink_pads)}"
        )

    return _make_validator(validate, PadConstraint(sink_pads=1))(arg)