Skip to content
Merged
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
38 changes: 5 additions & 33 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,7 @@ select = [
"RUF", # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf
]
fixable = ["ALL"]
# ANN002 - missing-type-args
# ANN003 - missing-type-kwargs
# D203 - one-blank-line-before-class
# D205 - blank-line-after-summary
# D206 - indent-with-spaces*
# D213 - multi-line-summary-second-line
# D300 - triple-single-quotes*
Expand All @@ -159,61 +156,36 @@ fixable = ["ALL"]
# PLC0206 - dict-index-missing-items
# *ignored for compatibility with formatter
ignore = [
"ANN002", "ANN003",
"D203", "D205", "D206", "D213", "D300", "D400", "D415",
"D203", "D206", "D213", "D300", "D400", "D415",
"E111", "E114", "E117", "E501",
"W191",
"S321",
"PLC0206",
]

[tool.ruff.lint.per-file-ignores]
# ANN001 - missing-type-function-argument
# ANN2 - missing-return-type
# D100 - undocumented-public-module
# D102 - undocumented-public-class
# D103 - undocumented-public-function
# ANN - type annotations
# D - docstrings
# S101 - assert
# B011 - assert-false
# INP001 - implicit-namespace-package
# SLF001 - private-member-access
"tests/*" = [
"ANN001",
"ANN2",
"D100",
"D102",
"D103",
"ANN",
"D",
"S101",
"B011",
"INP001",
"SLF001",
]
"src/ga4gh/vrs/normalize.py" = [
"ANN001",
"ANN201",
"ANN202",
]
"src/ga4gh/vrs/extras/object_store.py" = [
"ANN",
"D",
]
"src/ga4gh/vrs/enderef.py" = [
"ANN",
"D",
]
"src/ga4gh/vrs/dataproxy.py" = [
"B019",
]
"src/ga4gh/core/pydantic.py" = [
"ANN401",
]
"src/ga4gh/vrs/models.py" = [
"F", # https://docs.astral.sh/ruff/rules/#pyflakes-f
"E", "W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w
"I", # https://docs.astral.sh/ruff/rules/#isort-i
"N", # https://docs.astral.sh/ruff/rules/#pep8-naming-n
"D", # https://docs.astral.sh/ruff/rules/#pydocstyle-d
"ANN", # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann
"ASYNC", # https://docs.astral.sh/ruff/rules/#flake8-async-async
"S", # https://docs.astral.sh/ruff/rules/#flake8-bandit-s
"B", # https://docs.astral.sh/ruff/rules/#flake8-bugbear-b
Expand Down
13 changes: 8 additions & 5 deletions src/ga4gh/core/digests.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,26 @@


def sha512t24u(blob: bytes) -> str:
"""Generate a base64url-encode, truncated SHA-512 digest for given
binary data
"""Generate a base64url-encode, truncated SHA-512 digest for given binary data

The sha512t24u digest is a convention for constructing and
formatting digests for use as object identifiers. Specifically::
The sha512t24u digest is a convention for constructing and formatting digests for
use as object identifiers. Specifically::

* generate a SHA512 digest on binary data
* truncate at 24 bytes
* encode using base64url encoding

Examples:
For example:

>>> sha512t24u(b"")
'z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXc'

>>> sha512t24u(b"ACGT")
'aKF498dAxcJAqme6QYQ7EZ07-fiw8Kw2'

:param blob: Binary data to digest.
:return: URL-safe base64 encoding of the first 24 digest bytes.

"""
digest_size = 24
digest = hashlib.sha512(blob).digest()
Expand Down
61 changes: 43 additions & 18 deletions src/ga4gh/core/enderef.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@

These functions require a "class referable attribute" map, which will
typically be generated from the schema by
build_class_referable_attribute_map() in .models.py.
pydantic_class_refatt_map() in .models.py.

"""

import logging
from collections.abc import Mapping, MutableMapping, Sequence

from pydantic.main import BaseModel

Expand All @@ -25,31 +26,42 @@


def ga4gh_enref(
o, # noqa: ANN001
cra_map, # noqa: ANN001
object_store=None, # noqa: ANN001
o: BaseModel,
cra_map: Mapping[str, Sequence[str]],
object_store: MutableMapping[str, BaseModel] | None = None,
return_id_obj_tuple: bool = False,
) -> tuple:
"""Recursively convert "referable attributes" from inlined to
referenced form. Returns a new object.
) -> BaseModel | tuple[str | None, BaseModel]:
"""Recursively convert "referable attributes" from inlined to referenced form. Returns a new object.

:param o: VRS object
:param cra_map: class referrable-attribute map; { o.type: [attr, attr, ...] }
:param object_store: `collections.abc.MutableMapping` subclass that stores
objects as they are referenced. If not given, referenced objects are not stored.
:param return_id_obj_tuple: include ID with return value or not
:return: new, enref'd object, as well as object ID if param set
:raise TypeError: if any object IDs are non-GA4GH CURIEs
:raises TypeError: If a root-model CURIE reference has a non-GA4GH prefix.
Plain string references are left unchanged.
:raises ValueError: If the input is not an identifiable Pydantic model.
"""

def _id_and_store(o) -> str | None: # noqa: ANN001
def _id_and_store(o: BaseModel) -> str | None:
"""Compute an identifier and store the object when a store is available.

:param o: Object to process.
:return: Computed identifier, or None for an unidentifiable object.
"""
_id = ga4gh_identify(o)
if _id and object_store is not None:
object_store[_id] = o
return _id

def _enref(o: BaseModel) -> str | None:
"""depth-first recursive, in-place enref of object; returns id of object"""
"""Replace referable attributes in place and identify the visited object.

:param o: Object to process.
:return: Identifier of the updated object, or None.
:raises TypeError: If a root-model CURIE has a non-GA4GH prefix.
"""
ref_att_names = cra_map.get(o.type, [])
for ran in ref_att_names:
v = getattr(o, ran)
Expand Down Expand Up @@ -83,19 +95,32 @@ def _enref(o: BaseModel) -> str | None:
return (_id, o) if return_id_obj_tuple else o


def ga4gh_deref(o, cra_map, object_store) -> BaseModel: # noqa: ANN001
"""Convert "referable attributes" in-place from referenced to inlined
form.
def ga4gh_deref(
o: BaseModel,
cra_map: Mapping[str, Sequence[str]],
object_store: Mapping[str, BaseModel],
) -> BaseModel:
"""Copy an object and replace referable identifiers with inline objects.

`object_store` must be a mappable object and is required for
dereferencing.
`object_store` must be a mappable object and is required for dereferencing.

Raises KeyError if any object cannot be dereferenced
The top-level input is copied. Objects retrieved from the store are inserted
directly and may be mutated while their own references are resolved.

:param o: Object to process.
:param cra_map: Mapping from model names to referable attribute names.
:param object_store: Mapping from identifiers to referenced objects.
:return: Copy with identifier references replaced by objects.
:raises ValueError: If the input is not an identifiable Pydantic model.
:raises KeyError: If a referenced object is absent from the store.
"""

def _deref(o: BaseModel): # noqa: ANN202
"""depth-first recursive, in-place deref of object; returns id of object"""
def _deref(o: BaseModel) -> BaseModel:
"""Resolve references recursively, mutating each visited object.

:param o: Object to process.
:return: Updated object with inline references.
"""
if o.type not in cra_map:
_logger.warning("%s not in cra_map %s", o.type, cra_map)
return o
Expand Down
Loading
Loading