parse¶
Demo strategy class for text/json.
OPTIMADEParseStrategy
dataclass
¶
Parse strategy for JSON.
Implements strategies:
("mediaType", "application/vnd.optimade+json")
("mediaType", "application/vnd.OPTIMADE+json")
("mediaType", "application/vnd.OPTiMaDe+json")
("mediaType", "application/vnd.optimade+JSON")
("mediaType", "application/vnd.OPTIMADE+JSON")
("mediaType", "application/vnd.OPTiMaDe+JSON")
("mediaType", "application/vnd.optimade")
("mediaType", "application/vnd.OPTIMADE")
("mediaType", "application/vnd.OPTiMaDe")
Source code in oteapi_optimade/strategies/parse.py
@dataclass
class OPTIMADEParseStrategy:
"""Parse strategy for JSON.
**Implements strategies**:
- `("mediaType", "application/vnd.optimade+json")`
- `("mediaType", "application/vnd.OPTIMADE+json")`
- `("mediaType", "application/vnd.OPTiMaDe+json")`
- `("mediaType", "application/vnd.optimade+JSON")`
- `("mediaType", "application/vnd.OPTIMADE+JSON")`
- `("mediaType", "application/vnd.OPTiMaDe+JSON")`
- `("mediaType", "application/vnd.optimade")`
- `("mediaType", "application/vnd.OPTIMADE")`
- `("mediaType", "application/vnd.OPTiMaDe")`
"""
parse_config: OPTIMADEParseConfig
def initialize(
self,
session: dict[str, Any] | None = None, # noqa: ARG002
) -> SessionUpdate:
"""Initialize strategy.
This method will be called through the `/initialize` endpoint of the OTE-API
Services.
Parameters:
session: A session-specific dictionary context.
Returns:
An update model of key/value-pairs to be stored in the session-specific
context from services.
"""
return SessionUpdate()
def get(
self, session: SessionUpdate | dict[str, Any] | None = None
) -> OPTIMADEParseSession:
"""Request and parse an OPTIMADE response using OPT.
This method will be called through the strategy-specific endpoint of the
OTE-API Services.
Configuration values provided in `resource_config.configuration` take
precedence over the derived values from `downloadUrl`.
Workflow:
1. Request OPTIMADE response.
2. Parse as an OPTIMADE Python tools (OPT) pydantic response model.
Parameters:
session: A session-specific dictionary-like context.
Returns:
An update model of key/value-pairs to be stored in the session-specific
context from services.
"""
if session and isinstance(session, dict):
session = OPTIMADEParseSession(**session)
elif session and isinstance(session, SessionUpdate):
session = OPTIMADEParseSession(
**session.model_dump(exclude_defaults=True, exclude_unset=True)
)
else:
session = OPTIMADEParseSession()
if session.optimade_config:
self.parse_config.configuration.update(
session.optimade_config.model_dump(
exclude_defaults=True, exclude_unset=True
)
)
cache = DataCache(self.parse_config.configuration.datacache_config)
if self.parse_config.downloadUrl in cache:
response: dict[str, Any] = cache.get(self.parse_config.downloadUrl)
elif (
self.parse_config.configuration.datacache_config.accessKey
and self.parse_config.configuration.datacache_config.accessKey in cache
):
response = cache.get(
self.parse_config.configuration.datacache_config.accessKey
)
else:
download_config = self.parse_config.model_copy()
session.update(
create_strategy(StrategyType.DOWNLOAD, download_config).initialize(
session.model_dump(exclude_defaults=True, exclude_unset=True)
)
)
session.update(
create_strategy(StrategyType.DOWNLOAD, download_config).get(
session.model_dump(exclude_defaults=True, exclude_unset=True)
)
)
response = {"json": json.loads(cache.get(session.pop("key")))}
if (
not response.get("ok", True)
or (
response.get("status_code", 200) < 200
or response.get("status_code", 200) >= 300
)
or "errors" in response.get("json", {})
):
# Error response
try:
response_object = ErrorResponse(**response.get("json", {}))
except ValidationError as exc:
error_message = "Could not validate an error response."
LOGGER.error(
"%s\nValidationError: " "%s\nresponse=%r",
error_message,
exc,
response,
)
raise OPTIMADEParseError(error_message) from exc
else:
# Successful response
response_model = self.parse_config.downloadUrl.response_model()
LOGGER.debug("response_model=%r", response_model)
if response_model:
if not isinstance(response_model, tuple):
response_model = (response_model,)
for model_cls in response_model:
try:
response_object = model_cls(**response.get("json", {}))
except ValidationError:
pass
else:
break
else:
error_message = "Could not validate for an expected response model."
LOGGER.error(
"%s\nURL=%r\n" "response_models=%r\nresponse=%s",
error_message,
self.parse_config.downloadUrl,
response_model,
response,
)
raise OPTIMADEParseError(error_message)
else:
# No "endpoint" or unknown
LOGGER.debug("No response_model, using Success response model.")
try:
response_object = Success(**response.get("json", {}))
except ValidationError as exc:
error_message = "Unknown or unparseable endpoint."
LOGGER.error(
"%s\nValidatonError: %s\n"
"URL=%r\nendpoint=%r\nresponse_model=%r\nresponse=%s",
error_message,
exc,
self.parse_config.downloadUrl,
self.parse_config.downloadUrl.endpoint,
response_model,
response,
)
raise OPTIMADEParseError(error_message) from exc
session.optimade_response_model = (
response_object.__class__.__module__,
response_object.__class__.__name__,
)
session.optimade_response = response_object.model_dump(exclude_unset=True)
if session.optimade_config and session.optimade_config.query_parameters:
session = session.model_copy(
update={
"optimade_config": session.optimade_config.model_copy(
update={
"query_parameters": session.optimade_config.query_parameters.model_dump(
exclude_defaults=True,
exclude_unset=True,
)
}
)
}
)
if TYPE_CHECKING: # pragma: no cover
assert isinstance(session, OPTIMADEParseSession) # nosec
return session
get(self, session=None)
¶
Request and parse an OPTIMADE response using OPT.
This method will be called through the strategy-specific endpoint of the OTE-API Services.
Configuration values provided in resource_config.configuration
take
precedence over the derived values from downloadUrl
.
Workflow:
- Request OPTIMADE response.
- Parse as an OPTIMADE Python tools (OPT) pydantic response model.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
session |
SessionUpdate | dict[str, Any] | None |
A session-specific dictionary-like context. |
None |
Returns:
Type | Description |
---|---|
OPTIMADEParseSession |
An update model of key/value-pairs to be stored in the session-specific context from services. |
Source code in oteapi_optimade/strategies/parse.py
def get(
self, session: SessionUpdate | dict[str, Any] | None = None
) -> OPTIMADEParseSession:
"""Request and parse an OPTIMADE response using OPT.
This method will be called through the strategy-specific endpoint of the
OTE-API Services.
Configuration values provided in `resource_config.configuration` take
precedence over the derived values from `downloadUrl`.
Workflow:
1. Request OPTIMADE response.
2. Parse as an OPTIMADE Python tools (OPT) pydantic response model.
Parameters:
session: A session-specific dictionary-like context.
Returns:
An update model of key/value-pairs to be stored in the session-specific
context from services.
"""
if session and isinstance(session, dict):
session = OPTIMADEParseSession(**session)
elif session and isinstance(session, SessionUpdate):
session = OPTIMADEParseSession(
**session.model_dump(exclude_defaults=True, exclude_unset=True)
)
else:
session = OPTIMADEParseSession()
if session.optimade_config:
self.parse_config.configuration.update(
session.optimade_config.model_dump(
exclude_defaults=True, exclude_unset=True
)
)
cache = DataCache(self.parse_config.configuration.datacache_config)
if self.parse_config.downloadUrl in cache:
response: dict[str, Any] = cache.get(self.parse_config.downloadUrl)
elif (
self.parse_config.configuration.datacache_config.accessKey
and self.parse_config.configuration.datacache_config.accessKey in cache
):
response = cache.get(
self.parse_config.configuration.datacache_config.accessKey
)
else:
download_config = self.parse_config.model_copy()
session.update(
create_strategy(StrategyType.DOWNLOAD, download_config).initialize(
session.model_dump(exclude_defaults=True, exclude_unset=True)
)
)
session.update(
create_strategy(StrategyType.DOWNLOAD, download_config).get(
session.model_dump(exclude_defaults=True, exclude_unset=True)
)
)
response = {"json": json.loads(cache.get(session.pop("key")))}
if (
not response.get("ok", True)
or (
response.get("status_code", 200) < 200
or response.get("status_code", 200) >= 300
)
or "errors" in response.get("json", {})
):
# Error response
try:
response_object = ErrorResponse(**response.get("json", {}))
except ValidationError as exc:
error_message = "Could not validate an error response."
LOGGER.error(
"%s\nValidationError: " "%s\nresponse=%r",
error_message,
exc,
response,
)
raise OPTIMADEParseError(error_message) from exc
else:
# Successful response
response_model = self.parse_config.downloadUrl.response_model()
LOGGER.debug("response_model=%r", response_model)
if response_model:
if not isinstance(response_model, tuple):
response_model = (response_model,)
for model_cls in response_model:
try:
response_object = model_cls(**response.get("json", {}))
except ValidationError:
pass
else:
break
else:
error_message = "Could not validate for an expected response model."
LOGGER.error(
"%s\nURL=%r\n" "response_models=%r\nresponse=%s",
error_message,
self.parse_config.downloadUrl,
response_model,
response,
)
raise OPTIMADEParseError(error_message)
else:
# No "endpoint" or unknown
LOGGER.debug("No response_model, using Success response model.")
try:
response_object = Success(**response.get("json", {}))
except ValidationError as exc:
error_message = "Unknown or unparseable endpoint."
LOGGER.error(
"%s\nValidatonError: %s\n"
"URL=%r\nendpoint=%r\nresponse_model=%r\nresponse=%s",
error_message,
exc,
self.parse_config.downloadUrl,
self.parse_config.downloadUrl.endpoint,
response_model,
response,
)
raise OPTIMADEParseError(error_message) from exc
session.optimade_response_model = (
response_object.__class__.__module__,
response_object.__class__.__name__,
)
session.optimade_response = response_object.model_dump(exclude_unset=True)
if session.optimade_config and session.optimade_config.query_parameters:
session = session.model_copy(
update={
"optimade_config": session.optimade_config.model_copy(
update={
"query_parameters": session.optimade_config.query_parameters.model_dump(
exclude_defaults=True,
exclude_unset=True,
)
}
)
}
)
if TYPE_CHECKING: # pragma: no cover
assert isinstance(session, OPTIMADEParseSession) # nosec
return session
initialize(self, session=None)
¶
Initialize strategy.
This method will be called through the /initialize
endpoint of the OTE-API
Services.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
session |
dict[str, Any] | None |
A session-specific dictionary context. |
None |
Returns:
Type | Description |
---|---|
SessionUpdate |
An update model of key/value-pairs to be stored in the session-specific context from services. |
Source code in oteapi_optimade/strategies/parse.py
def initialize(
self,
session: dict[str, Any] | None = None, # noqa: ARG002
) -> SessionUpdate:
"""Initialize strategy.
This method will be called through the `/initialize` endpoint of the OTE-API
Services.
Parameters:
session: A session-specific dictionary context.
Returns:
An update model of key/value-pairs to be stored in the session-specific
context from services.
"""
return SessionUpdate()