Skip to content
Open
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
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[submodule "submodules/vrs"]
path = submodules/vrs
url = https://github.com/ga4gh/vrs.git
branch = 2.1
branch = 2.1.1-ballot.2026-09
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

## Features

- Pydantic implementation of GKS core models and VRS models
- Pydantic implementation of GKM-Core models and VRS models
- Algorithm for generating consistent, globally unique identifiers for variation without a central authority
- Algorithm for performing fully justified allele normalization
- Translating from and to other variant formats
Expand Down
9 changes: 8 additions & 1 deletion src/ga4gh/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@
use_ga4gh_compute_identifier_when,
)
from ga4gh.core.metadata import (
GKMMaturityMixin,
GKMMetadataMixin,
GKMSchemaMixin,
GKSMaturityMixin,
GKSMetadataMixin,
GKSSchemaMixin,
Maturity,
)
from ga4gh.core.models import GKSCoreMetadataMixin
from ga4gh.core.models import GKMCoreMetadataMixin, GKSCoreMetadataMixin
from ga4gh.core.pydantic import is_curie_type, is_pydantic_instance, pydantic_copy
from ga4gh.core.version import CORE_VERSION

Expand All @@ -45,6 +48,10 @@
"GA4GH_DIGEST_REGEXP",
"GA4GH_IR_REGEXP",
"GA4GH_PREFIX_SEP",
"GKMCoreMetadataMixin",
"GKMMaturityMixin",
"GKMMetadataMixin",
"GKMSchemaMixin",
"GKSCoreMetadataMixin",
"GKSMaturityMixin",
"GKSMetadataMixin",
Expand Down
85 changes: 63 additions & 22 deletions src/ga4gh/core/metadata.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"""Provide shared metadata types for GA4GH GKS models."""
"""Provide shared metadata types for GA4GH GKM models."""

from enum import Enum
from typing import Any, ClassVar

from pydantic.json_schema import GenerateJsonSchema, JsonSchemaMode
from typing_extensions import deprecated


class Maturity(str, Enum):
Expand All @@ -15,19 +16,19 @@ class Maturity(str, Enum):
DEPRECATED = "deprecated"


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

_maturity: ClassVar[Maturity]

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


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

_schema_base_uri: ClassVar[str] = "https://w3id.org/ga4gh/schema"
_product_name: ClassVar[str]
Expand All @@ -39,8 +40,44 @@ def schema_id(cls) -> str:
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."""
class GKMMetadataMixin(GKMMaturityMixin, GKMSchemaMixin):
"""Provide maturity and schema metadata for a GKM model."""

_abstract: ClassVar[bool] = False

@staticmethod
def apply_schema_metadata(
model_class: type, schema: dict[str, Any]
) -> dict[str, Any]:
"""Add GKM metadata to a generated JSON Schema.

:param model_class: Pydantic model class that produced the schema.
:param schema: Generated JSON Schema to annotate.
:returns: The annotated JSON Schema.
"""
schema["$id"] = model_class.schema_id()
schema["maturity"] = model_class.maturity().value

if model_class.__dict__.get("_abstract", False):
schema["abstract"] = True

# GA4GH identifier metadata is optional and applies only when declared.
ga4gh_class = getattr(model_class, "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

@classmethod
def model_json_schema(
Expand All @@ -50,30 +87,34 @@ def model_json_schema(
schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
mode: JsonSchemaMode = "validation",
) -> dict[str, Any]:
"""Generate JSON Schema with GKS metadata."""
"""Generate JSON Schema with GKM metadata.

:param by_alias: Whether to use field aliases.
:param ref_template: Template for schema references.
:param schema_generator: Pydantic schema generator class.
:param mode: Pydantic schema generation mode.
:returns: JSON Schema annotated with GKM 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
return cls.apply_schema_metadata(cls, schema)

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

ga4gh_metadata = {}
@deprecated("GKSMaturityMixin is deprecated; use GKMMaturityMixin instead.")
class GKSMaturityMixin(GKMMaturityMixin):
"""Deprecated alias for :class:`GKMMaturityMixin`."""

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

if inherent := getattr(ga4gh_class, "inherent", None):
ga4gh_metadata["inherent"] = list(inherent)
@deprecated("GKSSchemaMixin is deprecated; use GKMSchemaMixin instead.")
class GKSSchemaMixin(GKMSchemaMixin):
"""Deprecated alias for :class:`GKMSchemaMixin`."""

if ga4gh_metadata:
schema["ga4gh"] = ga4gh_metadata

return schema
@deprecated("GKSMetadataMixin is deprecated; use GKMMetadataMixin instead.")
class GKSMetadataMixin(GKMMetadataMixin):
"""Deprecated alias for :class:`GKMMetadataMixin`."""
37 changes: 24 additions & 13 deletions src/ga4gh/core/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""GKS Core Class Definitions"""
"""GKM Core Class Definitions"""

from __future__ import annotations

Expand All @@ -14,20 +14,25 @@
StringConstraints,
model_validator,
)
from typing_extensions import Self
from typing_extensions import Self, deprecated

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


class GKSCoreMetadataMixin(GKSMetadataMixin):
class GKMCoreMetadataMixin(GKMMetadataMixin):
"""Provide gkm-core model metadata."""

_product_name = "gkm-core"
_product_version = CORE_VERSION


@deprecated("GKSCoreMetadataMixin is deprecated; use GKMCoreMetadataMixin instead.")
class GKSCoreMetadataMixin(GKMCoreMetadataMixin):
"""Deprecated alias for :class:`GKMCoreMetadataMixin`."""


class BaseModelForbidExtra(BaseModel):
"""Base Pydantic model class with extra attributes forbidden."""

Expand Down Expand Up @@ -65,7 +70,7 @@ class MembershipOperator(str, Enum):
#########################################


class code(GKSCoreMetadataMixin, RootModel): # noqa: N801
class code(GKMCoreMetadataMixin, 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
Expand All @@ -83,7 +88,7 @@ class code(GKSCoreMetadataMixin, RootModel): # noqa: N801
)


class iriReference(GKSCoreMetadataMixin, RootModel): # noqa: N801
class iriReference(GKMCoreMetadataMixin, 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>`_.
Expand Down Expand Up @@ -115,13 +120,14 @@ def ga4gh_serialize(self) -> str: # noqa: D102
#########################################


class Entity(GKSMaturityMixin, BaseModel, ABC):
class Entity(GKMCoreMetadataMixin, 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
_abstract: ClassVar[bool] = True

id: str | None = Field(
default=None,
Expand All @@ -144,13 +150,14 @@ class Entity(GKSMaturityMixin, BaseModel, ABC):
)


class Element(GKSMaturityMixin, BaseModel, ABC):
class Element(GKMCoreMetadataMixin, 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
_abstract: ClassVar[bool] = True

id: str | None = Field(
default=None,
Expand All @@ -177,7 +184,7 @@ def get_extensions_by_name(self, name: str) -> list[Extension]:
#########################################


class Coding(GKSCoreMetadataMixin, Element, BaseModelForbidExtra):
class Coding(Element, BaseModelForbidExtra):
"""A structured representation of a code for a defined concept in a terminology or
code system.
"""
Expand All @@ -203,7 +210,7 @@ class Coding(GKSCoreMetadataMixin, Element, BaseModelForbidExtra):
)


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

model_config = ConfigDict(use_enum_values=True)
Expand All @@ -220,7 +227,7 @@ class ConceptMapping(GKSCoreMetadataMixin, Element, BaseModelForbidExtra):
)


class ConceptSet(GKSCoreMetadataMixin, Entity, BaseModelForbidExtra):
class ConceptSet(Entity, 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 @@ -236,6 +243,10 @@ class ConceptSet(GKSCoreMetadataMixin, Entity, BaseModelForbidExtra):
default="ConceptSet",
description='MUST be "ConceptSet".',
)
conceptSetType: str | None = Field( # noqa: N815
default=None,
description="A term indicating the type of concept being represented by the ConceptSet.",
)
concepts: list[MappableConcept] | list[ConceptSet] = Field(
...,
description="A list of concepts that are dependent (occurring together), or independent (existing separately), depending on the membership operator.",
Expand All @@ -247,7 +258,7 @@ class ConceptSet(GKSCoreMetadataMixin, Entity, BaseModelForbidExtra):
)


class Extension(GKSCoreMetadataMixin, Element, BaseModelForbidExtra):
class Extension(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
Expand All @@ -271,7 +282,7 @@ class Extension(GKSCoreMetadataMixin, Element, BaseModelForbidExtra):
)


class MappableConcept(GKSCoreMetadataMixin, Entity, BaseModelForbidExtra):
class MappableConcept(Entity, 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
Expand Down
2 changes: 1 addition & 1 deletion src/ga4gh/core/version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Define GKM-Core version"""

CORE_VERSION = "1.2.0"
CORE_VERSION = "1.3.0-ballot.2026-09.1"
Loading
Loading