Skip to content

baseinfo

SemanticVersion = Annotated[str, Field(pattern=SEMVER_PATTERN, examples=['0.10.1', '1.0.0-rc.2', '1.2.3-rc.5+develop'])] module-attribute

VERSIONED_BASE_URL_PATTERN = '^.+/v[0-1](\\.[0-9]+)*/?$' module-attribute

__all__ = ('AvailableApiVersion', 'BaseInfoAttributes', 'BaseInfoResource') module-attribute

AvailableApiVersion

Bases: BaseModel

A JSON object containing information about an available API version

Source code in optimade/models/baseinfo.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class AvailableApiVersion(BaseModel):
    """A JSON object containing information about an available API version"""

    url: Annotated[
        AnyHttpUrl,
        StrictField(
            description="A string specifying a versioned base URL that MUST adhere to the rules in section Base URL",
            json_schema_extra={
                "pattern": VERSIONED_BASE_URL_PATTERN,
            },
        ),
    ]

    version: Annotated[
        SemanticVersion,
        StrictField(
            description="""A string containing the full version number of the API served at that versioned base URL.
The version number string MUST NOT be prefixed by, e.g., 'v'.
Examples: `1.0.0`, `1.0.0-rc.2`.""",
        ),
    ]

    @field_validator("url", mode="after")
    @classmethod
    def url_must_be_versioned_base_Url(cls, value: AnyHttpUrl) -> AnyHttpUrl:
        """The URL must be a versioned base URL"""
        if not re.match(VERSIONED_BASE_URL_PATTERN, str(value)):
            raise ValueError(
                f"URL {value} must be a versioned base URL (i.e., must match the "
                f"pattern '{VERSIONED_BASE_URL_PATTERN}')"
            )
        return value

    @model_validator(mode="after")
    def crosscheck_url_and_version(self) -> "AvailableApiVersion":
        """Check that URL version and API version are compatible."""
        url = (
            str(self.url)
            .split("/")[-2 if str(self.url).endswith("/") else -1]
            .replace("v", "")
        )
        # as with version urls, we need to split any release tags or build metadata out of these URLs
        url_version = tuple(
            int(val) for val in url.split("-")[0].split("+")[0].split(".")
        )
        api_version = tuple(
            int(val) for val in str(self.version).split("-")[0].split("+")[0].split(".")
        )
        if any(a != b for a, b in zip(url_version, api_version)):
            raise ValueError(
                f"API version {api_version} is not compatible with url version {url_version}."
            )
        return self

url: Annotated[AnyHttpUrl, StrictField(description='A string specifying a versioned base URL that MUST adhere to the rules in section Base URL', json_schema_extra={pattern: VERSIONED_BASE_URL_PATTERN})] instance-attribute

version: Annotated[SemanticVersion, StrictField(description="A string containing the full version number of the API served at that versioned base URL.\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\nExamples: `1.0.0`, `1.0.0-rc.2`.")] instance-attribute

crosscheck_url_and_version()

Check that URL version and API version are compatible.

Source code in optimade/models/baseinfo.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@model_validator(mode="after")
def crosscheck_url_and_version(self) -> "AvailableApiVersion":
    """Check that URL version and API version are compatible."""
    url = (
        str(self.url)
        .split("/")[-2 if str(self.url).endswith("/") else -1]
        .replace("v", "")
    )
    # as with version urls, we need to split any release tags or build metadata out of these URLs
    url_version = tuple(
        int(val) for val in url.split("-")[0].split("+")[0].split(".")
    )
    api_version = tuple(
        int(val) for val in str(self.version).split("-")[0].split("+")[0].split(".")
    )
    if any(a != b for a, b in zip(url_version, api_version)):
        raise ValueError(
            f"API version {api_version} is not compatible with url version {url_version}."
        )
    return self

url_must_be_versioned_base_Url(value) classmethod

The URL must be a versioned base URL

Source code in optimade/models/baseinfo.py
39
40
41
42
43
44
45
46
47
48
@field_validator("url", mode="after")
@classmethod
def url_must_be_versioned_base_Url(cls, value: AnyHttpUrl) -> AnyHttpUrl:
    """The URL must be a versioned base URL"""
    if not re.match(VERSIONED_BASE_URL_PATTERN, str(value)):
        raise ValueError(
            f"URL {value} must be a versioned base URL (i.e., must match the "
            f"pattern '{VERSIONED_BASE_URL_PATTERN}')"
        )
    return value

BaseInfoAttributes

Bases: BaseModel

Attributes for Base URL Info endpoint

Source code in optimade/models/baseinfo.py
 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
111
112
113
114
115
116
117
118
119
120
121
122
123
class BaseInfoAttributes(BaseModel):
    """Attributes for Base URL Info endpoint"""

    api_version: Annotated[
        SemanticVersion,
        StrictField(
            description="""Presently used full version of the OPTIMADE API.
The version number string MUST NOT be prefixed by, e.g., "v".
Examples: `1.0.0`, `1.0.0-rc.2`.""",
        ),
    ]
    available_api_versions: Annotated[
        list[AvailableApiVersion],
        StrictField(
            description="A list of dictionaries of available API versions at other base URLs",
        ),
    ]
    formats: Annotated[
        list[str], StrictField(description="List of available output formats.")
    ] = ["json"]
    available_endpoints: Annotated[
        list[str],
        StrictField(
            description="List of available endpoints (i.e., the string to be appended to the versioned base URL).",
        ),
    ]
    entry_types_by_format: Annotated[
        dict[str, list[str]],
        StrictField(
            description="Available entry endpoints as a function of output formats."
        ),
    ]
    is_index: Annotated[
        Optional[bool],
        StrictField(
            description="If true, this is an index meta-database base URL (see section Index Meta-Database). "
            "If this member is not provided, the client MUST assume this is not an index meta-database base URL "
            "(i.e., the default is for `is_index` to be `false`).",
        ),
    ] = False

    @model_validator(mode="after")
    def formats_and_endpoints_must_be_valid(self) -> "BaseInfoAttributes":
        for format_, endpoints in self.entry_types_by_format.items():
            if format_ not in self.formats:
                raise ValueError(f"'{format_}' must be listed in formats to be valid")
            for endpoint in endpoints:
                if endpoint not in self.available_endpoints:
                    raise ValueError(
                        f"'{endpoint}' must be listed in available_endpoints to be valid"
                    )
        return self

api_version: Annotated[SemanticVersion, StrictField(description='Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., "v".\nExamples: `1.0.0`, `1.0.0-rc.2`.')] instance-attribute

available_api_versions: Annotated[list[AvailableApiVersion], StrictField(description='A list of dictionaries of available API versions at other base URLs')] instance-attribute

available_endpoints: Annotated[list[str], StrictField(description='List of available endpoints (i.e., the string to be appended to the versioned base URL).')] instance-attribute

entry_types_by_format: Annotated[dict[str, list[str]], StrictField(description='Available entry endpoints as a function of output formats.')] instance-attribute

formats: Annotated[list[str], StrictField(description='List of available output formats.')] = ['json'] class-attribute instance-attribute

is_index: Annotated[Optional[bool], StrictField(description='If true, this is an index meta-database base URL (see section Index Meta-Database). If this member is not provided, the client MUST assume this is not an index meta-database base URL (i.e., the default is for `is_index` to be `false`).')] = False class-attribute instance-attribute

formats_and_endpoints_must_be_valid()

Source code in optimade/models/baseinfo.py
113
114
115
116
117
118
119
120
121
122
123
@model_validator(mode="after")
def formats_and_endpoints_must_be_valid(self) -> "BaseInfoAttributes":
    for format_, endpoints in self.entry_types_by_format.items():
        if format_ not in self.formats:
            raise ValueError(f"'{format_}' must be listed in formats to be valid")
        for endpoint in endpoints:
            if endpoint not in self.available_endpoints:
                raise ValueError(
                    f"'{endpoint}' must be listed in available_endpoints to be valid"
                )
    return self

BaseInfoResource

Bases: Resource

Source code in optimade/models/baseinfo.py
126
127
128
129
class BaseInfoResource(Resource):
    id: Literal["/"] = "/"
    type: Literal["info"] = "info"
    attributes: BaseInfoAttributes

attributes: BaseInfoAttributes instance-attribute

id: Literal['/'] = '/' class-attribute instance-attribute

meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None class-attribute instance-attribute

model_config = ConfigDict(json_schema_extra=resource_json_schema_extra) class-attribute instance-attribute

relationships: Annotated[Optional[Relationships], StrictField(description='[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\ndescribing relationships between the resource and other JSON API resources.')] = None class-attribute instance-attribute

type: Literal['info'] = 'info' class-attribute instance-attribute

Resource

Bases: BaseResource

Resource objects appear in a JSON API document to represent resources.

Source code in optimade/models/jsonapi.py
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
class Resource(BaseResource):
    """Resource objects appear in a JSON API document to represent resources."""

    links: Annotated[
        Optional[ResourceLinks],
        StrictField(
            description="a links object containing links related to the resource."
        ),
    ] = None
    meta: Annotated[
        Optional[Meta],
        StrictField(
            description="a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.",
        ),
    ] = None
    attributes: Annotated[
        Optional[Attributes],
        StrictField(
            description="an attributes object representing some of the resource’s data.",
        ),
    ] = None
    relationships: Annotated[
        Optional[Relationships],
        StrictField(
            description="""[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)
describing relationships between the resource and other JSON API resources.""",
        ),
    ] = None

attributes: Annotated[Optional[Attributes], StrictField(description='an attributes object representing some of the resource’s data.')] = None class-attribute instance-attribute

id: Annotated[str, StrictField(description='Resource ID')] instance-attribute

meta: Annotated[Optional[Meta], StrictField(description='a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship.')] = None class-attribute instance-attribute

model_config = ConfigDict(json_schema_extra=resource_json_schema_extra) class-attribute instance-attribute

relationships: Annotated[Optional[Relationships], StrictField(description='[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\ndescribing relationships between the resource and other JSON API resources.')] = None class-attribute instance-attribute

type: Annotated[str, StrictField(description='Resource type')] instance-attribute

StrictField(default=PydanticUndefined, *, 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
default Any

The only non-keyword argument allowed for Field.

PydanticUndefined
description Optional[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
Any

The pydantic Field.

Source code in optimade/models/utils.py
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 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
111
112
113
114
115
116
117
118
119
def StrictField(
    default: "Any" = PydanticUndefined,
    *,
    description: Optional[str] = None,
    **kwargs: "Any",
) -> Any:
    """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:
        default: The only non-keyword argument allowed for 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_schema_and_field_keys = ["pattern"]

    allowed_keys = [
        "pattern",
        "uniqueItems",
    ] + 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({default!r}, **{kwargs!r}) with "
            f"forbidden keywords {_banned}."
        )

    # Handle description
    if description is None:
        warnings.warn(
            f"No description provided for StrictField specified by {default!r}, "
            f"**{kwargs!r}."
        )
    else:
        kwargs["description"] = description

    # OPTIMADE schema extensions
    json_schema_extra: dict[str, Any] = kwargs.pop("json_schema_extra", {})

    # Go through all JSON Schema keys and add them to the json_schema_extra.
    for key in allowed_keys:
        if key not in kwargs:
            continue

        # If they are OPTIMADE schema extensions, add them with the OPTIMADE prefix.
        schema_key = (
            f"{OPTIMADE_SCHEMA_EXTENSION_PREFIX}{key}"
            if key in OPTIMADE_SCHEMA_EXTENSION_KEYS
            else key
        )

        for key_variant in (key, schema_key):
            if key_variant in json_schema_extra:
                if json_schema_extra.pop(key_variant) != kwargs[key]:
                    raise RuntimeError(
                        f"Conflicting values for {key} in json_schema_extra and kwargs."
                    )

        json_schema_extra[schema_key] = (
            kwargs[key] if key in allowed_schema_and_field_keys else kwargs.pop(key)
        )

    kwargs["json_schema_extra"] = json_schema_extra

    return Field(default, **kwargs)