Prefix

class Prefix[source]

Bases: str

A string that is validated by Pydantic as a CURIE prefix.

This class is a subclass of Python’s built-in string class, so you can wrap any string with it:

from curies import Prefix

prefix = Prefix("CHEBI")

You can implicitly type annotate data with this class:

from curies import Prefix

prefix_map: dict[Prefix, str] = {
    "CHEBI": "http://purl.obolibrary.org/obo/CHEBI_",
}

You can more explicitly type annotate data with this class using Pydantic’s root model:

from pydantic import RootModel
from curies import Prefix

PrefixMap = RootModel[dict[Prefix, str]]

prefix_map = PrefixMap.model_validate(
    {
        "CHEBI": "http://purl.obolibrary.org/obo/CHEBI_",
    }
).root

This pattern is common enough that it’s included in curies.PrefixMap.

When used inside a Pydantic model, this class knows how to do validation that the prefix matches the regular expression for an XSD NCName. Here’s an example usage with Pydantic:

from curies import Prefix
from pydantic import BaseModel


class ResourceInfo(BaseModel):
    prefix: Prefix
    name: str


model = ResourceInfo.model_validate(
    {
        "prefix": "CHEBI",
        "name": "Chemical Entities of Biological Interest",
    }
)

# raises a pydantic.ValidationError, because the prefix
# doesn't match the NCName pattern
ResourceInfo.model_validate(
    {
        "prefix": "$nope",
        "name": "An invalid semantic space!",
    }
)

This class implements a hook that uses Pydantic’s “context” for validation that lets you pass a Converter to check for existence and standardization with respect to the context in the converter:

from curies import Prefix, get_obo_converter
from pydantic import BaseModel

class ResourceInfo(BaseModel):
    prefix: Prefix
    name: str

converter = get_obo_converter()
model = ResourceInfo.model_validate(
    {
        "prefix": "CHEBI",
        "name": "Chemical Entities of Biological Interest",
    },
    context=converter,
)

# raises a pydantic.ValidationError, because the prefix
# is not registered in the OBO Foundry, and is therefore
# not part of the OBO converter
ResourceInfo.model_validate(
    {
        "prefix": "efo",
        "name": "Experimental Factor Ontology",
    },
    context=converter,
)

# In case you need to pass more arbitrary
# context, you can also use a dict with the key
# "converter"
ResourceInfo.model_validate(
    {
        "prefix": "CHEBI",
        "name": "Chemical Entities of Biological Interest",
    },
    context={
        "converter": converter,
        ...
    },
)