Skip to content

types

Useful types and validators for use in pydantic models.

SemVerTuple module-attribute

SemVerTuple: TypeAlias = Tuple[
    NonNegativeInt, NonNegativeInt, NonNegativeInt
]

Type to be used for SemVer triples.

NonEmptyStr

Bases: FullMatch

Non-empty string (contains non-whitespace characters).

Source code in src/metador_core/schema/types.py
35
36
class NonEmptyStr(FullMatch, pattern=r"\s*\S[\S\s]*"):
    """Non-empty string (contains non-whitespace characters)."""

MimeTypeStr

Bases: NonEmptyStr

String that looks like a mime-type.

Source code in src/metador_core/schema/types.py
39
40
class MimeTypeStr(NonEmptyStr, pattern=r"[^ /;]+/[^ /;]+(;[^ /;]+)*"):
    """String that looks like a mime-type."""

HashsumStr

Bases: NonEmptyStr

String that looks like a hashsum.

Source code in src/metador_core/schema/types.py
43
44
class HashsumStr(NonEmptyStr, pattern="[0-9a-fA-F]+"):
    """String that looks like a hashsum."""

QualHashsumStr

Bases: HashsumStr

Hashsum string, prepended by the used algorithm.

Source code in src/metador_core/schema/types.py
50
51
class QualHashsumStr(HashsumStr, pattern=_hashalg_regex + r":[0-9a-fA-F]+"):
    """Hashsum string, prepended by the used algorithm."""

Duration

Bases: ParserMixin, Duration

ISO 8601 Duration.

Source code in src/metador_core/schema/types.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
@json_encoder(isodate.duration_isoformat)
class Duration(ParserMixin, isodate.Duration):
    """ISO 8601 Duration."""

    class Parser(BaseParser):
        schema_info = dict(
            title="string in ISO 8601 duration format",
            type="string",
            examples=["PT3H4M1S"],
        )

        @classmethod
        def parse(cls, tcls, v):
            if not isinstance(v, (str, tcls)):
                raise TypeError(f"Expected str or Duration, got {type(v)}.")
            # we have to force it into a Duration object,
            # otherwise we get possibly a timedelta back, which we do not want
            # because we want to serialize to the ISO format in both cases
            dur = isodate.parse_duration(v) if isinstance(v, str) else v
            return tcls(seconds=dur.total_seconds())

StringParser

Bases: BaseParser

Parser from string into some target class.

Source code in src/metador_core/schema/types.py
82
83
84
85
86
87
88
89
90
91
92
93
94
95
class StringParser(BaseParser):
    """Parser from string into some target class."""

    @classmethod
    def parse(cls, tcls, v):
        if isinstance(v, tcls):
            return v

        if not isinstance(v, str):
            msg = f"Expected str or {tcls.__name__}, got {type(v)}."
            raise TypeError(msg)

        ret = tcls(v)
        return ret

PintParser

Bases: StringParser

Shared base for PintUnit and PintQuantity, taking care of exceptions.

Source code in src/metador_core/schema/types.py
 98
 99
100
101
102
103
104
105
106
107
108
109
class PintParser(StringParser):
    """Shared base for `PintUnit` and `PintQuantity`, taking care of exceptions."""

    @classmethod
    def parse(cls, tcls, v):
        if not v:
            msg = f"Got empty string, expected {tcls.__name__}."
            raise ValueError(msg)
        try:
            return super().parse(tcls, v)
        except UndefinedUnitError as e:
            raise ValueError(str(e))

PintUnit

Bases: ParserMixin, Unit

pydantic-compatible pint.Unit.

Source code in src/metador_core/schema/types.py
112
113
114
115
116
117
118
119
120
121
@json_encoder(str)
class PintUnit(ParserMixin, Unit):
    """pydantic-compatible pint.Unit."""

    class Parser(PintParser):
        schema_info = dict(
            title="Physical unit compatible with the Python pint library.",
            type="string",
            examples=["meter * candela", "kilogram / second ** 2"],
        )

PintQuantity

Bases: ParserMixin, Quantity

pydantic-compatible pint.Quantity.

Source code in src/metador_core/schema/types.py
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
@json_encoder(str)
class PintQuantity(ParserMixin, Quantity):
    """pydantic-compatible pint.Quantity."""

    def __new__(cls, *args, **kwargs):
        # hack to make parsing work, for some reason it does not work without this
        # (it does not correctly identify the unit for some reason)
        if kwargs.get("passthrough"):
            return super().__new__(cls, *args)

        ret = Quantity(*args)  # ensure that the quantity is correctly parsed
        # return instance of the subclass:
        return cls(ret.m, ret.u, passthrough=True)

    class Parser(PintParser):
        schema_info = dict(
            title="Physical quantity compatible with the Python pint library.",
            type="string",
            examples=["5 meter * candela", "7.12 kilogram / second ** 2"],
        )