← 返回首页
optimade_json - OPTIMADE Python tools
Skip to content
OPTIMADE Python tools
optimade_json
optimade-python-tools
OPTIMADE Python tools

optimade_json

Modified JSON API v1.0 for OPTIMADE API

ValidIdentifier = Annotated[str, Field(pattern=IDENTIFIER_REGEX)] module-attribute

A type that constrains strings to valid OPTIMADE identifiers (e.g., property names, ID strings).

BaseRelationshipMeta

Bases: Meta

Specific meta field for base relationship resource

Source code in optimade/models/optimade_json.py
421 422 423 424 425 426 427 428 429
class BaseRelationshipMeta(jsonapi.Meta): """Specific meta field for base relationship resource""" description: Annotated[ str, StrictField( description="OPTIONAL human-readable description of the relationship." ), ]

description instance-attribute

model_config = ConfigDict(extra='allow') class-attribute instance-attribute

BaseRelationshipResource

Bases: BaseResource

Minimum requirements to represent a relationship resource

Source code in optimade/models/optimade_json.py
432 433 434 435 436 437 438 439 440
class BaseRelationshipResource(jsonapi.BaseResource): """Minimum requirements to represent a relationship resource""" meta: Annotated[ BaseRelationshipMeta | None, StrictField( description="Relationship meta field. MUST contain 'description' if supplied.", ), ] = None

id instance-attribute

meta = None class-attribute instance-attribute

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

type instance-attribute

DataType

Bases: Enum

Optimade Data types

See the section "Data types" in the OPTIMADE API specification for more information.

Source code in optimade/models/optimade_json.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 120 121 122 123 124 125 126 127 128 129 130 131 132
class DataType(Enum): """Optimade Data types See the section "Data types" in the OPTIMADE API specification for more information. """ STRING = "string" INTEGER = "integer" FLOAT = "float" BOOLEAN = "boolean" TIMESTAMP = "timestamp" LIST = "list" DICTIONARY = "dictionary" UNKNOWN = "unknown" @classmethod def get_values(cls) -> list[str]: """Get OPTIMADE data types (enum values) as a (sorted) list""" return sorted(_.value for _ in cls) @classmethod def from_python_type(cls, python_type: type | str | object) -> Optional["DataType"]: """Get OPTIMADE data type from a Python type""" mapping = { "bool": cls.BOOLEAN, "int": cls.INTEGER, "float": cls.FLOAT, "complex": None, "generator": cls.LIST, "list": cls.LIST, "tuple": cls.LIST, "range": cls.LIST, "hash": cls.INTEGER, "str": cls.STRING, "bytes": cls.STRING, "bytearray": None, "memoryview": None, "set": cls.LIST, "frozenset": cls.LIST, "dict": cls.DICTIONARY, "dict_keys": cls.LIST, "dict_values": cls.LIST, "dict_items": cls.LIST, "Nonetype": cls.UNKNOWN, "None": cls.UNKNOWN, "datetime": cls.TIMESTAMP, "date": cls.TIMESTAMP, "time": cls.TIMESTAMP, "datetime.datetime": cls.TIMESTAMP, "datetime.date": cls.TIMESTAMP, "datetime.time": cls.TIMESTAMP, } if isinstance(python_type, type): python_type = python_type.__name__ elif isinstance(python_type, object): if str(python_type) in mapping: python_type = str(python_type) else: python_type = type(python_type).__name__ return mapping.get(python_type, None) @classmethod def from_json_type(cls, json_type: str) -> Optional["DataType"]: """Get OPTIMADE data type from a named JSON type""" mapping = { "string": cls.STRING, "integer": cls.INTEGER, "number": cls.FLOAT, # actually includes both integer and float "object": cls.DICTIONARY, "array": cls.LIST, "boolean": cls.BOOLEAN, "null": cls.UNKNOWN, # OpenAPI "format"s: "double": cls.FLOAT, "float": cls.FLOAT, "int32": cls.INTEGER, "int64": cls.INTEGER, "date": cls.TIMESTAMP, "date-time": cls.TIMESTAMP, "password": cls.STRING, "byte": cls.STRING, "binary": cls.STRING, # Non-OpenAPI "format"s, but may still be used by pydantic/FastAPI "email": cls.STRING, "uuid": cls.STRING, "uri": cls.STRING, "hostname": cls.STRING, "ipv4": cls.STRING, "ipv6": cls.STRING, } return mapping.get(json_type, None)

BOOLEAN = 'boolean' class-attribute instance-attribute

DICTIONARY = 'dictionary' class-attribute instance-attribute

FLOAT = 'float' class-attribute instance-attribute

INTEGER = 'integer' class-attribute instance-attribute

LIST = 'list' class-attribute instance-attribute

STRING = 'string' class-attribute instance-attribute

TIMESTAMP = 'timestamp' class-attribute instance-attribute

UNKNOWN = 'unknown' class-attribute instance-attribute

from_json_type(json_type) classmethod

Get OPTIMADE data type from a named JSON type

Source code in optimade/models/optimade_json.py
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
@classmethod def from_json_type(cls, json_type: str) -> Optional["DataType"]: """Get OPTIMADE data type from a named JSON type""" mapping = { "string": cls.STRING, "integer": cls.INTEGER, "number": cls.FLOAT, # actually includes both integer and float "object": cls.DICTIONARY, "array": cls.LIST, "boolean": cls.BOOLEAN, "null": cls.UNKNOWN, # OpenAPI "format"s: "double": cls.FLOAT, "float": cls.FLOAT, "int32": cls.INTEGER, "int64": cls.INTEGER, "date": cls.TIMESTAMP, "date-time": cls.TIMESTAMP, "password": cls.STRING, "byte": cls.STRING, "binary": cls.STRING, # Non-OpenAPI "format"s, but may still be used by pydantic/FastAPI "email": cls.STRING, "uuid": cls.STRING, "uri": cls.STRING, "hostname": cls.STRING, "ipv4": cls.STRING, "ipv6": cls.STRING, } return mapping.get(json_type, None)

from_python_type(python_type) classmethod

Get OPTIMADE data type from a Python type

Source code in optimade/models/optimade_json.py
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
@classmethod def from_python_type(cls, python_type: type | str | object) -> Optional["DataType"]: """Get OPTIMADE data type from a Python type""" mapping = { "bool": cls.BOOLEAN, "int": cls.INTEGER, "float": cls.FLOAT, "complex": None, "generator": cls.LIST, "list": cls.LIST, "tuple": cls.LIST, "range": cls.LIST, "hash": cls.INTEGER, "str": cls.STRING, "bytes": cls.STRING, "bytearray": None, "memoryview": None, "set": cls.LIST, "frozenset": cls.LIST, "dict": cls.DICTIONARY, "dict_keys": cls.LIST, "dict_values": cls.LIST, "dict_items": cls.LIST, "Nonetype": cls.UNKNOWN, "None": cls.UNKNOWN, "datetime": cls.TIMESTAMP, "date": cls.TIMESTAMP, "time": cls.TIMESTAMP, "datetime.datetime": cls.TIMESTAMP, "datetime.date": cls.TIMESTAMP, "datetime.time": cls.TIMESTAMP, } if isinstance(python_type, type): python_type = python_type.__name__ elif isinstance(python_type, object): if str(python_type) in mapping: python_type = str(python_type) else: python_type = type(python_type).__name__ return mapping.get(python_type, None)

get_values() classmethod

Get OPTIMADE data types (enum values) as a (sorted) list

Source code in optimade/models/optimade_json.py
54 55 56 57
@classmethod def get_values(cls) -> list[str]: """Get OPTIMADE data types (enum values) as a (sorted) list""" return sorted(_.value for _ in cls)

Implementation

Bases: BaseModel

Information on the server implementation

Source code in optimade/models/optimade_json.py
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
class Implementation(BaseModel): """Information on the server implementation""" name: Annotated[ str | None, StrictField(description="name of the implementation") ] = None version: Annotated[ str | None, StrictField(description="version string of the current implementation"), ] = None homepage: Annotated[ jsonapi.JsonLinkType | None, StrictField( description="A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation.", ), ] = None source_url: Annotated[ jsonapi.JsonLinkType | None, StrictField( description="A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system.", ), ] = None maintainer: Annotated[ ImplementationMaintainer | None, StrictField( description="A dictionary providing details about the maintainer of the implementation.", ), ] = None issue_tracker: Annotated[ jsonapi.JsonLinkType | None, StrictField( description="A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker.", ), ] = None

homepage = None class-attribute instance-attribute

issue_tracker = None class-attribute instance-attribute

maintainer = None class-attribute instance-attribute

name = None class-attribute instance-attribute

source_url = None class-attribute instance-attribute

version = None class-attribute instance-attribute

ImplementationMaintainer

Bases: BaseModel

Details about the maintainer of the implementation

Source code in optimade/models/optimade_json.py
238 239 240 241 242 243
class ImplementationMaintainer(BaseModel): """Details about the maintainer of the implementation""" email: Annotated[ EmailStr, StrictField(description="the maintainer's email address") ]

email instance-attribute

OptimadeError

Bases: Error

detail MUST be present

Source code in optimade/models/optimade_json.py
135 136 137 138 139 140 141 142 143
class OptimadeError(jsonapi.Error): """detail MUST be present""" detail: Annotated[ str, StrictField( description="A human-readable explanation specific to this occurrence of the problem.", ), ]

code = None class-attribute instance-attribute

detail instance-attribute

id = None class-attribute instance-attribute

meta = None class-attribute instance-attribute

source = None class-attribute instance-attribute

status = None class-attribute instance-attribute

title = None class-attribute instance-attribute

__hash__()

Source code in optimade/models/jsonapi.py
191 192
def __hash__(self): return hash(self.model_dump_json())

Provider

Bases: BaseModel

Information on the database provider of the implementation.

Source code in optimade/models/optimade_json.py
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
class Provider(BaseModel): """Information on the database provider of the implementation.""" name: Annotated[ str, StrictField(description="a short name for the database provider") ] description: Annotated[ str, StrictField(description="a longer description of the database provider") ] prefix: Annotated[ str, StrictField( pattern=r"^[a-z]([a-z]|[0-9]|_)*$", description="database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes.", ), ] homepage: Annotated[ jsonapi.JsonLinkType | None, StrictField( description="a [JSON API links object](http://jsonapi.org/format/1.0#document-links) " "pointing to homepage of the database provider, either " "directly as a string, or as a link object.", ), ] = None

description instance-attribute

homepage = None class-attribute instance-attribute

name instance-attribute

prefix instance-attribute

Relationship

Bases: Relationship

Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.

Source code in optimade/models/optimade_json.py
443 444 445 446 447 448 449
class Relationship(jsonapi.Relationship): """Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.""" data: Annotated[ BaseRelationshipResource | list[BaseRelationshipResource] | None, StrictField(description="Resource linkage", uniqueItems=True), ] = None

data = None class-attribute instance-attribute

meta = None class-attribute instance-attribute

at_least_one_relationship_key_must_be_set()

Source code in optimade/models/jsonapi.py
279 280 281 282 283 284 285
@model_validator(mode="after") def at_least_one_relationship_key_must_be_set(self) -> "Relationship": if self.links is None and self.data is None and self.meta is None: raise ValueError( "Either 'links', 'data', or 'meta' MUST be specified for Relationship" ) return self

ResponseMeta

Bases: Meta

A JSON API meta member that contains JSON API meta objects of non-standard meta-information.

OPTIONAL additional information global to the query that is not specified in this document, MUST start with a database-provider-specific prefix.

Source code in optimade/models/optimade_json.py
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 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 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
class ResponseMeta(jsonapi.Meta): """ A [JSON API meta member](https://jsonapi.org/format/1.0#document-meta) that contains JSON API meta objects of non-standard meta-information. OPTIONAL additional information global to the query that is not specified in this document, MUST start with a database-provider-specific prefix. """ query: Annotated[ ResponseMetaQuery, StrictField(description="Information on the Query that was requested"), ] 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`.""", ), ] more_data_available: Annotated[ bool, StrictField( description="`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page).", ), ] # start of "SHOULD" fields for meta response optimade_schema: Annotated[ jsonapi.JsonLinkType | None, StrictField( alias="schema", description="""A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response. If it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema. It is possible that future versions of this specification allows for alternative schema types. Hence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors.""", ), ] = None time_stamp: Annotated[ datetime | None, StrictField( description="A timestamp containing the date and time at which the query was executed.", ), ] = None data_returned: Annotated[ int | None, StrictField( description="An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination.", ge=0, ), ] = None provider: Annotated[ Provider | None, StrictField( description="information on the database provider of the implementation." ), ] = None # start of "MAY" fields for meta response data_available: Annotated[ int | None, StrictField( description="An integer containing the total number of data resource objects available in the database for the endpoint.", ), ] = None last_id: Annotated[ str | None, StrictField(description="a string containing the last ID returned"), ] = None response_message: Annotated[ str | None, StrictField(description="response string from the server") ] = None request_delay: Annotated[ NonNegativeFloat | None, StrictField( description="""A non-negative float giving time in seconds that the client is suggested to wait before issuing a subsequent request. Implementation note: the functionality of this field overlaps to some degree with features provided by the HTTP error `429 Too Many Requests` and the `Retry-After` HTTP header. Implementations are suggested to provide consistent handling of request overload through both mechanisms.""" ), ] = None implementation: Annotated[ Implementation | None, StrictField(description="a dictionary describing the server implementation"), ] = None warnings: Annotated[ list[Warnings] | None, StrictField( description="""A list of warning resource objects representing non-critical errors or warnings. A warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `"warning"`. The field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features. The field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object. This is an exclusive field for error resource objects.""", uniqueItems=True, ), ] = None

api_version instance-attribute

data_available = None class-attribute instance-attribute

data_returned = None class-attribute instance-attribute

implementation = None class-attribute instance-attribute

last_id = None class-attribute instance-attribute

model_config = ConfigDict(extra='allow') class-attribute instance-attribute

more_data_available instance-attribute

optimade_schema = None class-attribute instance-attribute

provider = None class-attribute instance-attribute

query instance-attribute

request_delay = None class-attribute instance-attribute

response_message = None class-attribute instance-attribute

time_stamp = None class-attribute instance-attribute

warnings = None class-attribute instance-attribute

ResponseMetaQuery

Bases: BaseModel

Information on the query that was requested.

Source code in optimade/models/optimade_json.py
195 196 197 198 199 200 201 202 203 204 205 206
class ResponseMetaQuery(BaseModel): """Information on the query that was requested.""" representation: Annotated[ str, StrictField( description="""A string with the part of the URL following the versioned or unversioned base URL that serves the API. Query parameters that have not been used in processing the request MAY be omitted. In particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded. Example: `/structures?filter=nelements=2`""", ), ]

representation instance-attribute

Success

Bases: Response

errors are not allowed

Source code in optimade/models/optimade_json.py
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
class Success(jsonapi.Response): """errors are not allowed""" meta: Annotated[ ResponseMeta, StrictField(description="A meta object containing non-standard information"), ] @model_validator(mode="after") def either_data_meta_or_errors_must_be_set(self) -> "Success": """Overwriting the existing validation function, since 'errors' MUST NOT be set.""" required_fields = ("data", "meta") if not any(field in self.model_fields_set for field in required_fields): raise ValueError( f"At least one of {required_fields} MUST be specified in the top-level response." ) # errors MUST be skipped if self.errors or "errors" in self.model_fields_set: raise ValueError("'errors' MUST be skipped for a successful response.") return self

data = None class-attribute instance-attribute

errors = None class-attribute instance-attribute

included = None class-attribute instance-attribute

jsonapi = None class-attribute instance-attribute

meta instance-attribute

model_config = ConfigDict(json_encoders={datetime: lambda v: v.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')}) class-attribute instance-attribute

The specification mandates that datetimes must be encoded following RFC3339, which does not support fractional seconds, thus they must be stripped in the response. This can cause issues when the underlying database contains fields that do include microseconds, as filters may return unexpected results.

either_data_meta_or_errors_must_be_set()

Overwriting the existing validation function, since 'errors' MUST NOT be set.

Source code in optimade/models/optimade_json.py
405 406 407 408 409 410 411 412 413 414 415 416 417 418
@model_validator(mode="after") def either_data_meta_or_errors_must_be_set(self) -> "Success": """Overwriting the existing validation function, since 'errors' MUST NOT be set.""" required_fields = ("data", "meta") if not any(field in self.model_fields_set for field in required_fields): raise ValueError( f"At least one of {required_fields} MUST be specified in the top-level response." ) # errors MUST be skipped if self.errors or "errors" in self.model_fields_set: raise ValueError("'errors' MUST be skipped for a successful response.") return self

Warnings

Bases: OptimadeError

OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.

From the specification:

A warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value "warning". The field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.

Note: Must be named "Warnings", since "Warning" is a built-in Python class.

Source code in optimade/models/optimade_json.py
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
class Warnings(OptimadeError): """OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error. From the specification: A warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value "warning". The field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features. Note: Must be named "Warnings", since "Warning" is a built-in Python class. """ model_config = ConfigDict(json_schema_extra=warnings_json_schema_extra) type: Annotated[ Literal["warning"], StrictField( description='Warnings must be of type "warning"', pattern="^warning$", ), ] = "warning" @model_validator(mode="after") def status_must_not_be_specified(self) -> "Warnings": if self.status or "status" in self.model_fields_set: raise ValueError("status MUST NOT be specified for warnings") return self

code = None class-attribute instance-attribute

detail instance-attribute

id = None class-attribute instance-attribute

meta = None class-attribute instance-attribute

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

source = None class-attribute instance-attribute

status = None class-attribute instance-attribute

title = None class-attribute instance-attribute

type = 'warning' class-attribute instance-attribute

__hash__()

Source code in optimade/models/jsonapi.py
191 192
def __hash__(self): return hash(self.model_dump_json())

status_must_not_be_specified()

Source code in optimade/models/optimade_json.py
188 189 190 191 192
@model_validator(mode="after") def status_must_not_be_specified(self) -> "Warnings": if self.status or "status" in self.model_fields_set: raise ValueError("status MUST NOT be specified for warnings") return self

warnings_json_schema_extra(schema, model)

Update OpenAPI JSON schema model for Warning.

  • Ensure type is in the list required properties and in the correct place.
  • Remove status property. This property is not allowed for Warning, nor is it a part of the OPTIMADE definition of the Warning object.
Note

Since type is the last model field defined, it will simply be appended.

Source code in optimade/models/optimade_json.py
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
def warnings_json_schema_extra(schema: dict[str, Any], model: type["Warnings"]) -> None: """Update OpenAPI JSON schema model for `Warning`. * Ensure `type` is in the list required properties and in the correct place. * Remove `status` property. This property is not allowed for `Warning`, nor is it a part of the OPTIMADE definition of the `Warning` object. Note: Since `type` is the _last_ model field defined, it will simply be appended. """ if "required" in schema: if "type" not in schema["required"]: schema["required"].append("type") else: schema["required"] = ["type"] schema.get("properties", {}).pop("status", None)