"""Integration between the Bioregistry and Pydantic type annotations.
Semantic Pydantic is a wrapper around :class:`pydantic.Field` and
FastAPI's :mod:`fastapi.param_functions` like :class:`fastapi.Path`
that enables annotating the semantic space from which data in that field
comes via the Bioregistry.
"""
from __future__ import annotations
from collections.abc import Callable
from textwrap import dedent
from typing import Any
import bioregistry
from pydantic import Field
from pydantic.fields import FieldInfo
__all__ = [
"SemanticBody",
"SemanticField",
"SemanticForm",
"SemanticHeader",
"SemanticPath",
"SemanticQuery",
]
[docs]
def SemanticField(*args: Any, prefix: str, **kwargs: Any) -> FieldInfo: # noqa:N802
"""Create a Pydantic Field, annotated with a Bioregistry prefix."""
return _create(Field, *args, prefix=prefix, **kwargs)
[docs]
def SemanticBody(*args: Any, prefix: str, **kwargs: Any) -> FieldInfo: # noqa:N802
"""Create a FastAPI Body parameter, annotated with a Bioregistry prefix."""
from fastapi import Body
return _create(Body, *args, prefix=prefix, **kwargs)
[docs]
def SemanticQuery(*args: Any, prefix: str, **kwargs: Any) -> FieldInfo: # noqa:N802
"""Create a FastAPI Query parameter, annotated with a Bioregistry prefix."""
from fastapi import Query
return _create(Query, *args, prefix=prefix, **kwargs)
[docs]
def SemanticPath(*args: Any, prefix: str, **kwargs: Any) -> FieldInfo: # noqa:N802
"""Create a FastAPI Path parameter, annotated with a Bioregistry prefix."""
from fastapi import Path
return _create(Path, *args, prefix=prefix, **kwargs)
def _create(
func: Callable[..., FieldInfo],
*args: Any,
prefix: str,
**kwargs: Any,
) -> FieldInfo:
record = bioregistry.get_resource(prefix)
if record is None:
raise ValueError(
dedent(
f"""
Prefix is not registered in the Bioregistry: {prefix}. Please take one of following steps:
- Check the registry at https://bioregistry.io/registry for correct spelling
- Submit a new prefix request at https://github.com/biopragmatics/bioregistry/issues
"""
)
)
jse = kwargs.setdefault("json_schema_extra", {})
jse["bioregistry"] = {
"prefix": record.prefix,
"mappings": record.mappings,
}
if "title" not in kwargs:
kwargs["title"] = record.get_name()
if "description" not in kwargs:
kwargs["description"] = _get_description(record)
if pattern := record.get_pattern():
if "pattern" not in kwargs:
kwargs["pattern"] = pattern
if example := record.get_example():
if "example" not in kwargs:
jse["example"] = example
return func(*args, **kwargs)
def _get_description(record: bioregistry.Resource) -> str:
return f"""\
<p>\
This field corresponds to a local unique identifier from <i>{record.get_name()}</i></a>.
</p>\
<h4>Provenance</h4>\
<p>\
The semantics of this field are derived from the
<a href="https://bioregistry.io/{record.prefix}"><code>{record.prefix}</code></a> entry in
the <a href="https://bioregistry.io">Bioregistry</a>: a registry of semantic web and linked
open data compact URI (CURIE) prefixes and URI prefixes.
</p>\
<h4>Description of Semantic Space</h4>\
{record.get_description()}
""".strip()