Skip to content

utils

ANONYMOUS_ELEMENTS = tuple(itertools.islice(anonymous_element_generator(), 150)) module-attribute

Returns the first 150 values of the anonymous element generator.

ATOMIC_NUMBERS = {} module-attribute

CHEMICAL_FORMULA_REGEXP = '^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$' module-attribute

CHEMICAL_SYMBOLS = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og'] module-attribute

EXTRA_SYMBOLS = ['X', 'vacancy'] module-attribute

OPTIMADE_SCHEMA_EXTENSION_KEYS = ['support', 'queryable', 'unit', 'sortable'] module-attribute

OPTIMADE_SCHEMA_EXTENSION_PREFIX = 'x-optimade-' module-attribute

SemanticVersion

A custom type for a semantic version, using the recommended semver regexp from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string.

Source code in optimade/models/utils.py
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
class SemanticVersion(str):
    """A custom type for a semantic version, using the recommended
    semver regexp from
    https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string.

    """

    regex = re.compile(
        r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
    )

    @classmethod
    def __get_validators__(cls):
        yield cls.validate

    @classmethod
    def __modify_schema__(cls, field_schema):
        field_schema.update(
            pattern=cls.regex.pattern,
            example=["0.10.1", "1.0.0-rc.2", "1.2.3-rc.5+develop"],
        )

    @classmethod
    def validate(cls, v: str):
        if not cls.regex.match(v):
            raise ValueError(
                f"Unable to validate the version string {v!r} as a semantic version (expected <major>.<minor>.<patch>)."
                "See https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string for more information."
            )

        return v

    @property
    def _match(self):
        """The result of the regex match."""
        return self.regex.match(self)

    @property
    def major(self) -> int:
        """The major version number."""
        return int(self._match.group(1))

    @property
    def minor(self) -> int:
        """The minor version number."""
        return int(self._match.group(2))

    @property
    def patch(self) -> int:
        """The patch version number."""
        return int(self._match.group(3))

    @property
    def prerelease(self) -> str:
        """The pre-release tag."""
        return self._match.group(4)

    @property
    def build_metadata(self) -> str:
        """The build metadata."""
        return self._match.group(5)

    @property
    def base_version(self) -> str:
        """The base version string without patch and metadata info."""
        return f"{self.major}.{self.minor}.{self.patch}"

regex = re.compile('^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$') class-attribute

__get_validators__() classmethod

Source code in optimade/models/utils.py
163
164
165
@classmethod
def __get_validators__(cls):
    yield cls.validate

__modify_schema__(field_schema) classmethod

Source code in optimade/models/utils.py
167
168
169
170
171
172
@classmethod
def __modify_schema__(cls, field_schema):
    field_schema.update(
        pattern=cls.regex.pattern,
        example=["0.10.1", "1.0.0-rc.2", "1.2.3-rc.5+develop"],
    )

base_version() property

The base version string without patch and metadata info.

Source code in optimade/models/utils.py
214
215
216
217
@property
def base_version(self) -> str:
    """The base version string without patch and metadata info."""
    return f"{self.major}.{self.minor}.{self.patch}"

build_metadata() property

The build metadata.

Source code in optimade/models/utils.py
209
210
211
212
@property
def build_metadata(self) -> str:
    """The build metadata."""
    return self._match.group(5)

major() property

The major version number.

Source code in optimade/models/utils.py
189
190
191
192
@property
def major(self) -> int:
    """The major version number."""
    return int(self._match.group(1))

minor() property

The minor version number.

Source code in optimade/models/utils.py
194
195
196
197
@property
def minor(self) -> int:
    """The minor version number."""
    return int(self._match.group(2))

patch() property

The patch version number.

Source code in optimade/models/utils.py
199
200
201
202
@property
def patch(self) -> int:
    """The patch version number."""
    return int(self._match.group(3))

prerelease() property

The pre-release tag.

Source code in optimade/models/utils.py
204
205
206
207
@property
def prerelease(self) -> str:
    """The pre-release tag."""
    return self._match.group(4)

validate(v) classmethod

Source code in optimade/models/utils.py
174
175
176
177
178
179
180
181
182
@classmethod
def validate(cls, v: str):
    if not cls.regex.match(v):
        raise ValueError(
            f"Unable to validate the version string {v!r} as a semantic version (expected <major>.<minor>.<patch>)."
            "See https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string for more information."
        )

    return v

StrictFieldInfo

Wraps the standard pydantic FieldInfo in order to prefix any custom keys from StrictField.

Source code in optimade/models/utils.py
37
38
39
40
41
42
43
44
45
46
47
48
49
class StrictFieldInfo(FieldInfo):
    """Wraps the standard pydantic `FieldInfo` in order
    to prefix any custom keys from `StrictField`.

    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for key in OPTIMADE_SCHEMA_EXTENSION_KEYS:
            if key in self.extra:
                self.extra[f"{OPTIMADE_SCHEMA_EXTENSION_PREFIX}{key}"] = self.extra.pop(
                    key
                )

__init__(*args, **kwargs)

Source code in optimade/models/utils.py
43
44
45
46
47
48
49
def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    for key in OPTIMADE_SCHEMA_EXTENSION_KEYS:
        if key in self.extra:
            self.extra[f"{OPTIMADE_SCHEMA_EXTENSION_PREFIX}{key}"] = self.extra.pop(
                key
            )

SupportLevel

OPTIMADE property/field support levels

Source code in optimade/models/utils.py
29
30
31
32
33
34
class SupportLevel(Enum):
    """OPTIMADE property/field support levels"""

    MUST = "must"
    SHOULD = "should"
    OPTIONAL = "optional"

MUST = 'must' class-attribute

OPTIONAL = 'optional' class-attribute

SHOULD = 'should' class-attribute

OptimadeField(*args, support=None, queryable=None, unit=None, **kwargs)

A wrapper around pydantic.Field that adds OPTIMADE-specific field paramters queryable, support and unit, indicating the corresponding support level in the specification and the physical unit of the field.

Parameters:

Name Type Description Default
support Optional[SupportLevel]

The support level of the field itself, i.e. whether the field can be null or omitted by an implementation.

None
queryable Optional[SupportLevel]

The support level corresponding to the queryablility of this field.

None
unit Optional[str]

A string describing the unit of the field.

None

Returns:

Type Description
Field

The pydantic field with extra validation provided by StrictField.

Source code in optimade/models/utils.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
def OptimadeField(
    *args,
    support: Optional[SupportLevel] = None,
    queryable: Optional[SupportLevel] = None,
    unit: Optional[str] = None,
    **kwargs,
) -> Field:
    """A wrapper around `pydantic.Field` that adds OPTIMADE-specific
    field paramters `queryable`, `support` and `unit`, indicating
    the corresponding support level in the specification and the
    physical unit of the field.

    Arguments:
        support: The support level of the field itself, i.e. whether the field
            can be null or omitted by an implementation.
        queryable: The support level corresponding to the queryablility
            of this field.
        unit: A string describing the unit of the field.

    Returns:
        The pydantic field with extra validation provided by [`StrictField`][optimade.models.utils.StrictField].

    """

    # Collect non-null keyword arguments to add to the Field schema
    if unit is not None:
        kwargs["unit"] = unit
    if queryable is not None:
        if isinstance(queryable, str):
            queryable = SupportLevel(queryable.lower())
        kwargs["queryable"] = queryable
    if support is not None:
        if isinstance(support, str):
            support = SupportLevel(support.lower())
        kwargs["support"] = support

    return StrictField(*args, **kwargs)

StrictField(*args, description=None, **kwargs)

A wrapper around pydantic.Field that does the following:

  • Forbids any "extra" keys that would be passed to pydantic.Field, except those used elsewhere to modify the schema in-place, e.g. "uniqueItems", "pattern" and those added by OptimadeField, e.g. "unit", "queryable" and "sortable".
  • Emits a warning when no description is provided.

Parameters:

Name Type Description Default
*args Any

Positional arguments passed through to Field.

()
description str

The description of the Field; if this is not specified then a UserWarning will be emitted.

None
**kwargs Any

Extra keyword arguments to be passed to Field.

{}

Raises:

Type Description
RuntimeError

If **kwargs contains a key not found in the function signature of Field, or in the extensions used by models in this package (see above).

Returns:

Type Description
StrictFieldInfo

The pydantic Field.

Source code in optimade/models/utils.py
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
def StrictField(
    *args: "Any",
    description: str = None,
    **kwargs: "Any",
) -> StrictFieldInfo:
    """A wrapper around `pydantic.Field` that does the following:

    - Forbids any "extra" keys that would be passed to `pydantic.Field`,
      except those used elsewhere to modify the schema in-place,
      e.g. "uniqueItems", "pattern" and those added by OptimadeField, e.g.
      "unit", "queryable" and "sortable".
    - Emits a warning when no description is provided.

    Arguments:
        *args: Positional arguments passed through to `Field`.
        description: The description of the `Field`; if this is not
            specified then a `UserWarning` will be emitted.
        **kwargs: Extra keyword arguments to be passed to `Field`.

    Raises:
        RuntimeError: If `**kwargs` contains a key not found in the
            function signature of `Field`, or in the extensions used
            by models in this package (see above).

    Returns:
        The pydantic `Field`.

    """

    allowed_keys = [
        "pattern",
        "uniqueItems",
        "nullable",
    ] + OPTIMADE_SCHEMA_EXTENSION_KEYS
    _banned = [k for k in kwargs if k not in set(_PYDANTIC_FIELD_KWARGS + allowed_keys)]

    if _banned:
        raise RuntimeError(
            f"Not creating StrictField({args}, {kwargs}) with forbidden keywords {_banned}."
        )

    if description is not None:
        kwargs["description"] = description

    if description is None:
        warnings.warn(
            f"No description provided for StrictField specified by {args}, {kwargs}."
        )

    return StrictPydanticField(*args, **kwargs)

StrictPydanticField(*args, **kwargs)

Wrapper for Field that uses StrictFieldInfo instead of the pydantic FieldInfo.

Source code in optimade/models/utils.py
52
53
54
55
56
57
58
def StrictPydanticField(*args, **kwargs):
    """Wrapper for `Field` that uses `StrictFieldInfo` instead of
    the pydantic `FieldInfo`.
    """
    field_info = StrictFieldInfo(*args, **kwargs)
    field_info._validate()
    return field_info

anonymous_element_generator()

Generator that yields the next symbol in the A, B, Aa, ... Az naming scheme.

Source code in optimade/models/utils.py
220
221
222
223
224
225
226
227
228
def anonymous_element_generator():
    """Generator that yields the next symbol in the A, B, Aa, ... Az naming scheme."""
    from string import ascii_lowercase

    for size in itertools.count(1):
        for s in itertools.product(ascii_lowercase, repeat=size):
            s = list(s)
            s[0] = s[0].upper()
            yield "".join(s)