Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions src/ga4gh/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Python support used across GA4GH projects"""

from importlib.metadata import PackageNotFoundError, version
from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as package_version

import ga4gh.core.models as core_models
from ga4gh.core.digests import sha512t24u
Expand All @@ -19,16 +20,23 @@
is_ga4gh_identifier,
use_ga4gh_compute_identifier_when,
)
from ga4gh.core.metadata import (
GKSMaturityMixin,
GKSMetadataMixin,
GKSSchemaMixin,
Maturity,
)
from ga4gh.core.models import GKSCoreMetadataMixin
from ga4gh.core.pydantic import is_curie_type, is_pydantic_instance, pydantic_copy
from ga4gh.core.version import CORE_VERSION

try:
__version__ = version(__name__)
__version__ = package_version(__name__)
except PackageNotFoundError: # pragma: nocover
__version__ = "unknown"
finally:
del version, PackageNotFoundError
del package_version, PackageNotFoundError

CORE_VERSION = "1.1.0"

__all__ = [
"CORE_VERSION",
Expand All @@ -37,6 +45,11 @@
"GA4GH_DIGEST_REGEXP",
"GA4GH_IR_REGEXP",
"GA4GH_PREFIX_SEP",
"GKSCoreMetadataMixin",
"GKSMaturityMixin",
"GKSMetadataMixin",
"GKSSchemaMixin",
"Maturity",
"PrevVrsVersion",
"VrsObjectIdentifierIs",
"core_models",
Expand Down
79 changes: 79 additions & 0 deletions src/ga4gh/core/metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""Provide shared metadata types for GA4GH GKS models."""

from enum import Enum
from typing import Any, ClassVar

from pydantic.json_schema import GenerateJsonSchema, JsonSchemaMode


class Maturity(str, Enum):
"""Maturity levels for GA4GH product features."""

DRAFT = "draft"
TRIAL_USE = "trial use"
NORMATIVE = "normative"
DEPRECATED = "deprecated"


class GKSMaturityMixin:
"""Provide maturity metadata for a GA4GH GKS model."""

_maturity: ClassVar[Maturity]

@classmethod
def maturity(cls) -> Maturity:
"""Return the GKS maturity level for the model."""
return cls._maturity


class GKSSchemaMixin:
"""Provide a canonical JSON Schema identifier for a GA4GH GKS model."""

_schema_base_uri: ClassVar[str] = "https://w3id.org/ga4gh/schema"
_product_name: ClassVar[str]
_product_version: ClassVar[str]

@classmethod
def schema_id(cls) -> str:
"""Return the canonical JSON Schema identifier for the model."""
return f"{cls._schema_base_uri}/{cls._product_name}/{cls._product_version}/json/{cls.__name__}"


class GKSMetadataMixin(GKSMaturityMixin, GKSSchemaMixin):
"""Provide maturity and schema metadata for a concrete GKS model."""

@classmethod
def model_json_schema(
cls,
by_alias: bool = True,
ref_template: str = "#/$defs/{model}",
schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
mode: JsonSchemaMode = "validation",
) -> dict[str, Any]:
"""Generate JSON Schema with GKS metadata."""
schema = super().model_json_schema(
by_alias=by_alias,
ref_template=ref_template,
schema_generator=schema_generator,
mode=mode,
)

schema["$id"] = cls.schema_id()
schema["maturity"] = cls.maturity().value

ga4gh_class = getattr(cls, "ga4gh", None)
if not ga4gh_class:
return schema

ga4gh_metadata = {}

if prefix := getattr(ga4gh_class, "prefix", None):
ga4gh_metadata["prefix"] = prefix

if inherent := getattr(ga4gh_class, "inherent", None):
ga4gh_metadata["inherent"] = list(inherent)

if ga4gh_metadata:
schema["ga4gh"] = ga4gh_metadata

return schema
47 changes: 37 additions & 10 deletions src/ga4gh/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from abc import ABC
from enum import Enum
from typing import Annotated, Any, Literal
from typing import Annotated, Any, ClassVar, Literal

from pydantic import (
BaseModel,
Expand All @@ -17,6 +17,15 @@
from typing_extensions import Self

from ga4gh.core.identifiers import GA4GH_IR_REGEXP
from ga4gh.core.metadata import GKSMaturityMixin, GKSMetadataMixin, Maturity
from ga4gh.core.version import CORE_VERSION


class GKSCoreMetadataMixin(GKSMetadataMixin):
"""Provide GKS-Core model metadata."""

_product_name = "gks-core"
_product_version = CORE_VERSION


class BaseModelForbidExtra(BaseModel):
Expand Down Expand Up @@ -56,13 +65,15 @@ class MembershipOperator(str, Enum):
#########################################


class code(RootModel): # noqa: N801
class code(GKSCoreMetadataMixin, RootModel): # noqa: N801
"""Indicates that the value is taken from a set of controlled strings defined
elsewhere. Technically, a code is restricted to a string which has at least one
character and no leading or trailing whitespace, and where there is no whitespace
other than single spaces in the contents.
"""

_maturity: ClassVar[Maturity] = Maturity.TRIAL_USE

root: Annotated[str, StringConstraints(pattern=r"\S+( \S+)*")] = Field(
...,
json_schema_extra={
Expand All @@ -72,14 +83,16 @@ class code(RootModel): # noqa: N801
)


class iriReference(RootModel): # noqa: N801
class iriReference(GKSCoreMetadataMixin, RootModel): # noqa: N801
"""An IRI Reference (either an IRI or a relative-reference), according to `RFC3986
section 4.1 <https://datatracker.ietf.org/doc/html/rfc3986#section-4.1>`_ and
`RFC3987 section 2.1 <https://datatracker.ietf.org/doc/html/rfc3987#section-2.1>`_.
MAY be a JSON Pointer as an IRI fragment, as described by `RFC6901 section 6
<https://datatracker.ietf.org/doc/html/rfc6901#section-6>`_.
"""

_maturity: ClassVar[Maturity] = Maturity.TRIAL_USE

def __hash__(self) -> int: # noqa: D105
return self.root.__hash__()

Expand All @@ -102,12 +115,14 @@ def ga4gh_serialize(self) -> str: # noqa: D102
#########################################


class Entity(BaseModel, ABC):
class Entity(GKSMaturityMixin, BaseModel, ABC):
"""Anything that exists, has existed, or will exist.

Abstract base class to be extended by other classes. Do NOT instantiate directly.
"""

_maturity: ClassVar[Maturity] = Maturity.TRIAL_USE

id: str | None = Field(
default=None,
description="The 'logical' identifier of the Entity in the system of record, e.g. a UUID. This 'id' is unique within a given system, but may or may not be globally unique outside the system. It is used within a system to reference an object from another.",
Expand All @@ -129,12 +144,14 @@ class Entity(BaseModel, ABC):
)


class Element(BaseModel, ABC):
class Element(GKSMaturityMixin, BaseModel, ABC):
"""The base definition for all identifiable data objects.

Abstract base class to be extended by other classes. Do NOT instantiate directly.
"""

_maturity: ClassVar[Maturity] = Maturity.TRIAL_USE

id: str | None = Field(
default=None,
description="The 'logical' identifier of the data element in the system of record, e.g. a UUID. This 'id' is unique within a given system, but may or may not be globally unique outside the system. It is used within a system to reference an object from another.",
Expand All @@ -160,11 +177,13 @@ def get_extensions_by_name(self, name: str) -> list[Extension]:
#########################################


class Coding(Element, BaseModelForbidExtra):
class Coding(GKSCoreMetadataMixin, Element, BaseModelForbidExtra):
"""A structured representation of a code for a defined concept in a terminology or
code system.
"""

_maturity: ClassVar[Maturity] = Maturity.TRIAL_USE

name: str | None = Field(
default=None,
description="The human-readable name for the coded concept, as defined by the code system.",
Expand All @@ -184,11 +203,13 @@ class Coding(Element, BaseModelForbidExtra):
)


class ConceptMapping(Element, BaseModelForbidExtra):
class ConceptMapping(GKSCoreMetadataMixin, Element, BaseModelForbidExtra):
"""A mapping to a concept in a terminology or code system."""

model_config = ConfigDict(use_enum_values=True)

_maturity: ClassVar[Maturity] = Maturity.TRIAL_USE

coding: Coding = Field(
...,
description="A structured representation of a code for a defined concept in a terminology or code system.",
Expand All @@ -199,7 +220,7 @@ class ConceptMapping(Element, BaseModelForbidExtra):
)


class ConceptSet(Element, BaseModelForbidExtra):
class ConceptSet(GKSCoreMetadataMixin, Element, BaseModelForbidExtra):
"""A set of concepts that may be considered as dependent (occurring together), or
independent (existing separately) in the context of some knowledge reported about
them, as indicated by a set membership operator. e.g. a set of independent molecular
Expand All @@ -209,6 +230,8 @@ class ConceptSet(Element, BaseModelForbidExtra):

model_config = ConfigDict(use_enum_values=True)

_maturity: ClassVar[Maturity] = Maturity.TRIAL_USE

type: Literal["ConceptSet"] = Field(
default="ConceptSet",
description='MUST be "ConceptSet".',
Expand All @@ -224,14 +247,16 @@ class ConceptSet(Element, BaseModelForbidExtra):
)


class Extension(Element, BaseModelForbidExtra):
class Extension(GKSCoreMetadataMixin, Element, BaseModelForbidExtra):
"""The Extension class provides entities with a means to include additional
attributes that are outside of the specified standard but needed by a given content
provider or system implementer. These extensions are not expected to be natively
understood, but may be used for pre-negotiated exchange of message attributes
between systems.
"""

_maturity: ClassVar[Maturity] = Maturity.TRIAL_USE

name: str = Field(
...,
description="A name for the Extension. Should be indicative of its meaning and/or the type of information it value represents.",
Expand All @@ -246,9 +271,11 @@ class Extension(Element, BaseModelForbidExtra):
)


class MappableConcept(Element, BaseModelForbidExtra):
class MappableConcept(GKSCoreMetadataMixin, Element, BaseModelForbidExtra):
"""A concept based on a primaryCoding and/or name that may be mapped to one or more other `Codings`."""

_maturity: ClassVar[Maturity] = Maturity.TRIAL_USE

conceptType: str | None = Field( # noqa: N815
default=None,
description="A term indicating the type of concept being represented by the MappableConcept.",
Expand Down
3 changes: 3 additions & 0 deletions src/ga4gh/core/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""Define GKS-Core version"""

CORE_VERSION = "1.1.0"
10 changes: 5 additions & 5 deletions src/ga4gh/vrs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
"""Public interface to the GA4GH Variation Representation reference implementation"""

from importlib.metadata import PackageNotFoundError, version
from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as package_version

from ga4gh.vrs import models
from ga4gh.vrs.enderef import vrs_deref, vrs_enref
from ga4gh.vrs.models import VrsType
from ga4gh.vrs.normalize import normalize
from ga4gh.vrs.version import VRS_VERSION

try:
__version__ = version(__name__)
__version__ = package_version(__name__)
except PackageNotFoundError: # pragma: nocover
__version__ = "unknown"
finally:
del version, PackageNotFoundError
del package_version, PackageNotFoundError


VRS_VERSION = "2.1.0-snapshot.2026-02.2"

__all__ = [
"VRS_VERSION",
"VrsType",
Expand Down
Loading
Loading