From a7eed183c281ba2666b289b39429390bec899aa1 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Thu, 28 May 2026 13:25:56 -0400 Subject: [PATCH 01/21] first pass at implementing liftover requests --- src/anyvlm/anyvar/base_client.py | 26 +++++++++-- src/anyvlm/anyvar/http_client.py | 72 ++++++++++++++++++++++++------ src/anyvlm/anyvar/python_client.py | 13 +++++- src/anyvlm/functions/get_caf.py | 42 ++++++++++++----- tests/unit/anyvar/test_clients.py | 6 ++- 5 files changed, 128 insertions(+), 31 deletions(-) diff --git a/src/anyvlm/anyvar/base_client.py b/src/anyvlm/anyvar/base_client.py index 2679aae..daa4152 100644 --- a/src/anyvlm/anyvar/base_client.py +++ b/src/anyvlm/anyvar/base_client.py @@ -3,6 +3,7 @@ import abc from collections.abc import Iterable, Sequence +from anyvar.core.objects import VrsObject from anyvar.mapping.liftover import ReferenceAssembly from ga4gh.vrs.models import Allele @@ -22,10 +23,18 @@ class BaseAnyVarClient(abc.ABC): """Interface elements for an AnyVar client""" @abc.abstractmethod - def get_registered_allele( + def retrieve_allele_by_id(self, vrs_id: str) -> VrsObject | None: + """Retrieve VRS Allele for given VRS ID + + :param vrs_id: The ID to dereference + :return: The VRS Allele, or `None` if unable to retrieve the Allele. + """ + + @abc.abstractmethod + def retrieve_allele_by_expression( self, expression: str, assembly: ReferenceAssembly = ReferenceAssembly.GRCH38 ) -> Allele | None: - """Retrieve registered VRS Allele for given allele expression + """Retrieve VRS Allele for given allele expression Currently, only expressions supported by the VRS-Python translator are supported. This could change depending on the AnyVar implementation, though, and probably @@ -33,7 +42,7 @@ def get_registered_allele( :param expression: variation expression to get VRS Allele for :param assembly: reference assembly used in expression - :return: VRS Allele if translation succeeds and VRS Allele has already been registered, else `None` + :return: VRS Allele if translation succeeds, else `None` """ @abc.abstractmethod @@ -54,6 +63,17 @@ def put_allele_expressions( else `None`, for the i'th expression """ + @abc.abstractmethod + def get_liftover_variation_id( + self, vrs_id: str, starting_assembly: ReferenceAssembly + ) -> str | None: + """Get the VRS ID for the lifted-over equivalent of the variation specified by the provided VRS ID. + + :param vrs_id: The VRS ID of the variation to lift over + :param starting_assembly: The assembly to liftover FROM (i.e., the assembly of the starting variant) + :return: The VRS ID of the lifted-over variation, or `None` if liftover is unsuccessful + """ + @abc.abstractmethod def close(self) -> None: """Clean up AnyVar connection.""" diff --git a/src/anyvlm/anyvar/http_client.py b/src/anyvlm/anyvar/http_client.py index b5ce003..82a572a 100644 --- a/src/anyvlm/anyvar/http_client.py +++ b/src/anyvlm/anyvar/http_client.py @@ -6,8 +6,14 @@ from typing import Literal import requests +from anyvar.core.metadata import VariationMapping +from anyvar.core.objects import VrsObject from anyvar.mapping.liftover import ReferenceAssembly -from anyvar.restapi.schema import GetObjectResponse, RegisterVariationResponse +from anyvar.restapi.schema import ( + GetMappingResponse, + GetObjectResponse, + RegisterVariationResponse, +) from ga4gh.vrs import VrsType, models from anyvlm.anyvar.base_client import ( @@ -36,9 +42,11 @@ def __init__( def _make_http_request( self, - method: Literal[HTTPMethod.POST] | Literal[HTTPMethod.PUT], + method: Literal[HTTPMethod.POST] + | Literal[HTTPMethod.PUT] + | Literal[HTTPMethod.GET], url: str, - payload: dict | list, + payload: dict | list | None = None, ) -> requests.Response: """Issue an HTTP request to an AnyVar server. @@ -71,10 +79,25 @@ def _make_http_request( raise return response - def get_registered_allele( + def retrieve_allele_by_id(self, vrs_id: str) -> VrsObject | None: + """Retrieve a VRS Allele by ID + + :param vrs_id: The ID of the VRS Allele to retrieve + :return: The full VRS Allele. + """ + url = f"{self.hostname}/object/{vrs_id}" + try: + response = self._make_http_request(method=HTTPMethod.GET, url=url) + except requests.HTTPError: + return None # TODO: add logging + + validated_response: GetObjectResponse = GetObjectResponse(**response.json()) + return validated_response.data + + def retrieve_allele_by_expression( self, expression: str, assembly: ReferenceAssembly = ReferenceAssembly.GRCH38 ) -> models.Allele | None: - """Retrieve registered VRS Allele for given allele expression + """Retrieve VRS Allele for given allele expression Currently, only expressions supported by the VRS-Python translator are supported. This could change depending on the AnyVar implementation, though, and probably @@ -82,7 +105,7 @@ def get_registered_allele( :param expression: variation expression to get VRS Allele for :param assembly: reference assembly used in expression - :return: VRS Allele if translation succeeds and VRS Allele has already been registered, else `None` + :return: VRS Allele if translation succeeds, else `None` """ url = f"{self.hostname}/variation" payload = { @@ -91,7 +114,7 @@ def get_registered_allele( "input_type": VrsType.ALLELE.value, } try: - response = self._make_http_request(HTTPMethod.POST, url, payload) + response = self._make_http_request(HTTPMethod.PUT, url, payload) except requests.HTTPError as e: if e.response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY: _logger.debug( @@ -99,13 +122,8 @@ def get_registered_allele( ) return None - if e.response.status_code == HTTPStatus.NOT_FOUND: - _logger.debug("No variation found for expression '%s'", expression) - return None - raise AnyVarClientError from e - - validated_response = GetObjectResponse(**response.json()) - return validated_response.data # type: ignore (input_type=Allele guarantees return type) + validated_response = RegisterVariationResponse(**response.json()) # type ignore + return validated_response.object # type: ignore (input_type=Allele guarantees return type) def put_allele_expressions( self, @@ -139,6 +157,32 @@ def put_allele_expressions( raise AnyVarClientError from e return [RegisterVariationResponse(**r).object_id for r in response.json()] + def get_liftover_variation_id( + self, vrs_id: str, starting_assembly: ReferenceAssembly + ) -> str | None: + """Get the VRS ID for the lifted-over equivalent of the variation specified by the provided VRS ID. + + :param vrs_id: The VRS ID of the variation to lift over + :param starting_assembly: The assembly to liftover FROM (i.e., the assembly of the starting variant) + :return: The VRS ID of the lifted-over variation, or `None` if liftover is unsuccessful + """ + as_source: bool = starting_assembly == ReferenceAssembly.GRCH37 + url: str = f"{self.hostname}/object/{vrs_id}/mappings/liftover_to?as_source={as_source}" + try: + response = self._make_http_request(HTTPMethod.GET, url) + except requests.HTTPError: + return None + # TODO - handle this (raise exception or return qqch) + + validated_response: GetMappingResponse = GetMappingResponse(**response.json()) + if len(validated_response.mappings) > 1: + pass + # raise Exception # TODO - use more specific exception + + mapping_result: VariationMapping = validated_response.mappings[0] + + return mapping_result.dest_id + def close(self) -> None: """Clean up AnyVar connection. diff --git a/src/anyvlm/anyvar/python_client.py b/src/anyvlm/anyvar/python_client.py index 2a3d2a8..666fcb8 100644 --- a/src/anyvlm/anyvar/python_client.py +++ b/src/anyvlm/anyvar/python_client.py @@ -53,7 +53,7 @@ def _translate_allele_expression( _logger.exception("Failed to translate expression: %s", expression) return translated_variation # type: ignore - def get_registered_allele( + def retrieve_allele_by_expression( self, expression: str, assembly: ReferenceAssembly = ReferenceAssembly.GRCH38 ) -> Allele | None: """Retrieve registered VRS Allele for given allele expression @@ -109,6 +109,17 @@ def put_allele_expressions( results.append(None) return results + def get_liftover_variation_id( + self, vrs_id: str, starting_assembly: ReferenceAssembly + ) -> str | None: + """Get the VRS ID for the lifted-over equivalent of the variation specified by the provided VRS ID. + + :param vrs_id: The VRS ID of the variation to lift over + :param starting_assembly: The assembly to liftover FROM (i.e., the assembly of the starting variant) + :return: The VRS ID of the lifted-over variation, or `None` if liftover is unsuccessful + """ + # TODO - fill this in + def close(self) -> None: """Clean up AnyVar instance.""" _logger.info("Closing AnyVar client.") diff --git a/src/anyvlm/functions/get_caf.py b/src/anyvlm/functions/get_caf.py index b62994c..57538f8 100644 --- a/src/anyvlm/functions/get_caf.py +++ b/src/anyvlm/functions/get_caf.py @@ -3,6 +3,7 @@ import logging from ga4gh.core.models import iriReference +from ga4gh.vrs.models import Allele from anyvlm.anyvar.base_client import BaseAnyVarClient from anyvlm.storage.base_storage import Storage @@ -22,6 +23,20 @@ class VariantNotRegisteredError(Exception): """Raised when a variant is not registered in the AnyVar client""" +def _retrieve_caf_with_resolved_alleles( + variation: Allele, anyvlm_storage: Storage +) -> list[AnyVlmCohortAlleleFrequencyResult]: + cafs: list[AnyVlmCohortAlleleFrequencyResult] = ( + anyvlm_storage.get_caf_by_vrs_allele_id(vrs_allele_id=variation.id) + ) + + for caf in cafs: + if isinstance(caf.focusAllele, iriReference): + caf.focusAllele = variation + + return cafs + + def get_caf( anyvar_client: BaseAnyVarClient, anyvlm_storage: Storage, @@ -53,18 +68,23 @@ def get_caf( msg = "Unsupported assembly ID: {assembly_id}" raise ValueError(msg) from e - vrs_variation = anyvar_client.get_registered_allele(gnomad_vcf, assembly) - if not vrs_variation: - msg = f"Variant {assembly.value} {gnomad_vcf} is not registered in AnyVar" - _logger.debug(msg) - raise VariantNotRegisteredError(msg) - - cafs: list[AnyVlmCohortAlleleFrequencyResult] = ( - anyvlm_storage.get_caf_by_vrs_allele_id(vrs_variation.id) # type: ignore + vrs_variation = anyvar_client.retrieve_allele_by_expression(gnomad_vcf, assembly) + cafs: list[AnyVlmCohortAlleleFrequencyResult] = _retrieve_caf_with_resolved_alleles( + variation=vrs_variation, anyvlm_storage=anyvlm_storage ) - for caf in cafs: - if isinstance(caf.focusAllele, iriReference): - caf.focusAllele = vrs_variation + liftover_vrs_id: str = anyvar_client.get_liftover_variation_id( + vrs_id=vrs_variation.id, starting_assembly=assembly + ) + liftover_variation: Allele | None = anyvar_client.retrieve_allele_by_id( + vrs_id=liftover_vrs_id + ) + if liftover_variation: + liftover_cafs: list[AnyVlmCohortAlleleFrequencyResult] = ( + _retrieve_caf_with_resolved_alleles( + variation=liftover_variation, anyvlm_storage=anyvlm_storage + ) + ) + cafs.extend(liftover_cafs) return cafs diff --git a/tests/unit/anyvar/test_clients.py b/tests/unit/anyvar/test_clients.py index 0e8fca4..d447c0b 100644 --- a/tests/unit/anyvar/test_clients.py +++ b/tests/unit/anyvar/test_clients.py @@ -62,7 +62,9 @@ def test_get_registered_allele_expressions_unpopulated( if "vcf_expression" not in allele_fixture: continue assert ( - anyvar_client.get_registered_allele(allele_fixture["vcf_expression"]) + anyvar_client.retrieve_allele_by_expression( + allele_fixture["vcf_expression"] + ) is None ) @@ -76,7 +78,7 @@ def test_get_registered_allele_expressions_populated( for allele_fixture in alleles.values(): if "vcf_expression" not in allele_fixture: continue - assert anyvar_client.get_registered_allele( + assert anyvar_client.retrieve_allele_by_expression( allele_fixture["vcf_expression"] ) == models.Allele(**allele_fixture["variation"]) From aa75e7d0950f8c12169220832704bfbc91e89788 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Thu, 28 May 2026 13:35:21 -0400 Subject: [PATCH 02/21] update 'retrieve_allele_by_expression' in HTTP client to prevent 'response' possibly being unbound --- src/anyvlm/anyvar/http_client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/anyvlm/anyvar/http_client.py b/src/anyvlm/anyvar/http_client.py index 82a572a..f87f2f3 100644 --- a/src/anyvlm/anyvar/http_client.py +++ b/src/anyvlm/anyvar/http_client.py @@ -15,6 +15,7 @@ RegisterVariationResponse, ) from ga4gh.vrs import VrsType, models +from requests.models import Response from anyvlm.anyvar.base_client import ( AnyVarClientConnectionError, @@ -114,13 +115,13 @@ def retrieve_allele_by_expression( "input_type": VrsType.ALLELE.value, } try: - response = self._make_http_request(HTTPMethod.PUT, url, payload) + response: Response = self._make_http_request(HTTPMethod.PUT, url, payload) except requests.HTTPError as e: if e.response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY: _logger.debug( "Translation failed for variant expression '%s'", expression ) - return None + return None validated_response = RegisterVariationResponse(**response.json()) # type ignore return validated_response.object # type: ignore (input_type=Allele guarantees return type) From 6de4087a4903ee9ffa04a661de0b5267850ec687 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Thu, 28 May 2026 13:39:39 -0400 Subject: [PATCH 03/21] add definition of 'retreive_allele_by_id' for Python client --- src/anyvlm/anyvar/http_client.py | 6 +++--- src/anyvlm/anyvar/python_client.py | 13 +++++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/anyvlm/anyvar/http_client.py b/src/anyvlm/anyvar/http_client.py index f87f2f3..af10c5a 100644 --- a/src/anyvlm/anyvar/http_client.py +++ b/src/anyvlm/anyvar/http_client.py @@ -81,10 +81,10 @@ def _make_http_request( return response def retrieve_allele_by_id(self, vrs_id: str) -> VrsObject | None: - """Retrieve a VRS Allele by ID + """Retrieve VRS Allele for given VRS ID - :param vrs_id: The ID of the VRS Allele to retrieve - :return: The full VRS Allele. + :param vrs_id: The ID to dereference + :return: The VRS Allele, or `None` if unable to retrieve the Allele. """ url = f"{self.hostname}/object/{vrs_id}" try: diff --git a/src/anyvlm/anyvar/python_client.py b/src/anyvlm/anyvar/python_client.py index 666fcb8..59bab3d 100644 --- a/src/anyvlm/anyvar/python_client.py +++ b/src/anyvlm/anyvar/python_client.py @@ -4,6 +4,7 @@ from collections.abc import Iterable, Sequence from anyvar import AnyVar +from anyvar.core.objects import VrsObject from anyvar.mapping.liftover import ReferenceAssembly from anyvar.restapi.schema import SupportedVariationType from anyvar.storage.base import Storage @@ -53,10 +54,18 @@ def _translate_allele_expression( _logger.exception("Failed to translate expression: %s", expression) return translated_variation # type: ignore + def retrieve_allele_by_id(self, vrs_id: str) -> VrsObject | None: + """Retrieve VRS Allele for given VRS ID + + :param vrs_id: The ID to dereference + :return: The VRS Allele, or `None` if unable to retrieve the Allele. + """ + return self.av.get_object(object_id=vrs_id, object_type=Allele) + def retrieve_allele_by_expression( self, expression: str, assembly: ReferenceAssembly = ReferenceAssembly.GRCH38 ) -> Allele | None: - """Retrieve registered VRS Allele for given allele expression + """Retrieve VRS Allele for given allele expression Currently, only expressions supported by the VRS-Python translator are supported. This could change depending on the AnyVar implementation, though, and probably @@ -64,7 +73,7 @@ def retrieve_allele_by_expression( :param expression: variation expression to get VRS Allele for :param assembly: reference assembly used in expression - :return: VRS Allele if translation succeeds and VRS Allele has already been registered, else `None` + :return: VRS Allele if translation succeeds, else `None` """ translated_variation = self._translate_allele_expression(expression, assembly) if not translated_variation: From 4ed709d25526e8e1d6a920315883bd5d623cb6cd Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Thu, 28 May 2026 14:20:56 -0400 Subject: [PATCH 04/21] fix HTTP implementation of 'get_liftover_variation_id' and add Python client implementation --- src/anyvlm/anyvar/http_client.py | 2 +- src/anyvlm/anyvar/python_client.py | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/anyvlm/anyvar/http_client.py b/src/anyvlm/anyvar/http_client.py index af10c5a..fb1b09a 100644 --- a/src/anyvlm/anyvar/http_client.py +++ b/src/anyvlm/anyvar/http_client.py @@ -182,7 +182,7 @@ def get_liftover_variation_id( mapping_result: VariationMapping = validated_response.mappings[0] - return mapping_result.dest_id + return mapping_result.dest_id if as_source else mapping_result.source_id def close(self) -> None: """Clean up AnyVar connection. diff --git a/src/anyvlm/anyvar/python_client.py b/src/anyvlm/anyvar/python_client.py index 59bab3d..e97f6d8 100644 --- a/src/anyvlm/anyvar/python_client.py +++ b/src/anyvlm/anyvar/python_client.py @@ -4,6 +4,7 @@ from collections.abc import Iterable, Sequence from anyvar import AnyVar +from anyvar.core.metadata import VariationMapping, VariationMappingType from anyvar.core.objects import VrsObject from anyvar.mapping.liftover import ReferenceAssembly from anyvar.restapi.schema import SupportedVariationType @@ -127,7 +128,19 @@ def get_liftover_variation_id( :param starting_assembly: The assembly to liftover FROM (i.e., the assembly of the starting variant) :return: The VRS ID of the lifted-over variation, or `None` if liftover is unsuccessful """ - # TODO - fill this in + as_source: bool = starting_assembly == ReferenceAssembly.GRCH37 + liftover_mappings: Iterable[VariationMapping] = self.av.get_object_mappings( + source_object_id=vrs_id, mapping_type=VariationMappingType.LIFTOVER + ) + # liftover_mappings: VariationMapping = self.av.get_object_mappings(source_object_id=vrs_id, mapping_type=VariationMappingType.LIFTOVER, as_source=as_source) + liftover_mapping: VariationMapping | None = next( + iter(liftover_mappings), None + ) # TODO: replace this line with the one above once AnyVar version is updated + return ( + (liftover_mapping.dest_id if as_source else liftover_mapping.source_id) + if liftover_mapping + else None + ) def close(self) -> None: """Clean up AnyVar instance.""" From 46016090fbf7230ba976e9640b5d604189366201 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Thu, 28 May 2026 14:36:03 -0400 Subject: [PATCH 05/21] fix tests --- tests/unit/functions/test_ingest_vcf.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/unit/functions/test_ingest_vcf.py b/tests/unit/functions/test_ingest_vcf.py index b0e071b..dc81e8d 100644 --- a/tests/unit/functions/test_ingest_vcf.py +++ b/tests/unit/functions/test_ingest_vcf.py @@ -2,7 +2,9 @@ from pathlib import Path import pytest +from anyvar.core.objects import VrsObject from anyvar.mapping.liftover import ReferenceAssembly +from ga4gh.vrs.models import Allele from anyvlm.anyvar.base_client import BaseAnyVarClient from anyvlm.functions.ingest_vcf import VcfAfColumnsError, ingest_vcf @@ -44,11 +46,18 @@ def stub_anyvar_client(): } class TestAnyVarClient(BaseAnyVarClient): - def get_registered_allele( + def retrieve_allele_by_id( + self, + vrs_id: str, + starting_assembly: ReferenceAssembly = ReferenceAssembly.GRCH38, + ) -> VrsObject | None: + raise NotImplementedError + + def retrieve_allele_by_expression( self, expression: str, assembly: ReferenceAssembly = ReferenceAssembly.GRCH38, - ): + ) -> Allele | None: raise NotImplementedError def put_allele_expressions( @@ -61,6 +70,11 @@ def put_allele_expressions( for expr in expressions ] + def get_liftover_variation_id( + self, vrs_id: str, starting_assembly: ReferenceAssembly + ) -> str | None: + raise NotImplementedError + def close(self) -> None: """Clean up AnyVar connection.""" From 7e1c18974aa231caa5a03f453141cf2c4fe58894 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Fri, 29 May 2026 14:03:36 -0400 Subject: [PATCH 06/21] clean up 'get_caf' to handle edge cases where variant/variant id are 'None' --- .gitignore | 4 ++ src/anyvlm/anyvar/base_client.py | 4 +- src/anyvlm/anyvar/http_client.py | 4 +- src/anyvlm/anyvar/python_client.py | 4 +- src/anyvlm/functions/get_caf.py | 58 ++++++++++++++++++++++--- src/anyvlm/utils/types.py | 2 +- tests/unit/functions/test_ingest_vcf.py | 4 +- 7 files changed, 64 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index ff290af..2e2f92a 100644 --- a/.gitignore +++ b/.gitignore @@ -220,3 +220,7 @@ uv.lock # Mac things: .DS_Store + + +# UTA download +uta_20241220.pgd.gz diff --git a/src/anyvlm/anyvar/base_client.py b/src/anyvlm/anyvar/base_client.py index daa4152..3836c50 100644 --- a/src/anyvlm/anyvar/base_client.py +++ b/src/anyvlm/anyvar/base_client.py @@ -3,7 +3,7 @@ import abc from collections.abc import Iterable, Sequence -from anyvar.core.objects import VrsObject +from anyvar.core.objects import VrsVariation from anyvar.mapping.liftover import ReferenceAssembly from ga4gh.vrs.models import Allele @@ -23,7 +23,7 @@ class BaseAnyVarClient(abc.ABC): """Interface elements for an AnyVar client""" @abc.abstractmethod - def retrieve_allele_by_id(self, vrs_id: str) -> VrsObject | None: + def retrieve_allele_by_id(self, vrs_id: str) -> VrsVariation | None: """Retrieve VRS Allele for given VRS ID :param vrs_id: The ID to dereference diff --git a/src/anyvlm/anyvar/http_client.py b/src/anyvlm/anyvar/http_client.py index fb1b09a..8442df8 100644 --- a/src/anyvlm/anyvar/http_client.py +++ b/src/anyvlm/anyvar/http_client.py @@ -7,7 +7,7 @@ import requests from anyvar.core.metadata import VariationMapping -from anyvar.core.objects import VrsObject +from anyvar.core.objects import VrsVariation from anyvar.mapping.liftover import ReferenceAssembly from anyvar.restapi.schema import ( GetMappingResponse, @@ -80,7 +80,7 @@ def _make_http_request( raise return response - def retrieve_allele_by_id(self, vrs_id: str) -> VrsObject | None: + def retrieve_allele_by_id(self, vrs_id: str) -> VrsVariation | None: """Retrieve VRS Allele for given VRS ID :param vrs_id: The ID to dereference diff --git a/src/anyvlm/anyvar/python_client.py b/src/anyvlm/anyvar/python_client.py index e97f6d8..ae8a8a6 100644 --- a/src/anyvlm/anyvar/python_client.py +++ b/src/anyvlm/anyvar/python_client.py @@ -5,7 +5,7 @@ from anyvar import AnyVar from anyvar.core.metadata import VariationMapping, VariationMappingType -from anyvar.core.objects import VrsObject +from anyvar.core.objects import VrsVariation from anyvar.mapping.liftover import ReferenceAssembly from anyvar.restapi.schema import SupportedVariationType from anyvar.storage.base import Storage @@ -55,7 +55,7 @@ def _translate_allele_expression( _logger.exception("Failed to translate expression: %s", expression) return translated_variation # type: ignore - def retrieve_allele_by_id(self, vrs_id: str) -> VrsObject | None: + def retrieve_allele_by_id(self, vrs_id: str) -> VrsVariation | None: """Retrieve VRS Allele for given VRS ID :param vrs_id: The ID to dereference diff --git a/src/anyvlm/functions/get_caf.py b/src/anyvlm/functions/get_caf.py index 57538f8..e713e6b 100644 --- a/src/anyvlm/functions/get_caf.py +++ b/src/anyvlm/functions/get_caf.py @@ -2,6 +2,7 @@ import logging +from anyvar.core.objects import VrsVariation from ga4gh.core.models import iriReference from ga4gh.vrs.models import Allele @@ -23,9 +24,45 @@ class VariantNotRegisteredError(Exception): """Raised when a variant is not registered in the AnyVar client""" +class VariantLookupError(Exception): + """Raised when a variant cannot be retrieved from AnyVar""" + + +class IncompleteVariantError(Exception): + """Raised when a variant is missing one or more properties required by AnyVLM""" + + +class UnexpectedVariantTypeError(Exception): + """Raised when .type is not of the type expected by AnyVLM""" + + +def _validate_allele(variant: VrsVariation | None) -> Allele: + if not variant: + raise VariantLookupError + + variant_id: str | None = variant.id + if not variant_id: + raise IncompleteVariantError + + if not variant.type == "Allele": + raise UnexpectedVariantTypeError + + return variant + + def _retrieve_caf_with_resolved_alleles( variation: Allele, anyvlm_storage: Storage ) -> list[AnyVlmCohortAlleleFrequencyResult]: + """Retrieve CAF data for a resolved allele. + + :param variation: The allele to retrieve CAF data for + :param anyvlm_storage: The storage for this AnyVLM instance + :return: A list of AnyVlmCohortAlleleFrequencyResult objects + """ + if not variation.id: + error_message: str = "Variants must include an 'id'" + raise IncompleteVariantError(error_message) + cafs: list[AnyVlmCohortAlleleFrequencyResult] = ( anyvlm_storage.get_caf_by_vrs_allele_id(vrs_allele_id=variation.id) ) @@ -68,23 +105,30 @@ def get_caf( msg = "Unsupported assembly ID: {assembly_id}" raise ValueError(msg) from e - vrs_variation = anyvar_client.retrieve_allele_by_expression(gnomad_vcf, assembly) + vrs_variation: Allele = _validate_allele( + variant=anyvar_client.retrieve_allele_by_expression(gnomad_vcf, assembly) + ) + cafs: list[AnyVlmCohortAlleleFrequencyResult] = _retrieve_caf_with_resolved_alleles( variation=vrs_variation, anyvlm_storage=anyvlm_storage ) - liftover_vrs_id: str = anyvar_client.get_liftover_variation_id( - vrs_id=vrs_variation.id, starting_assembly=assembly + liftover_vrs_id: str | None = anyvar_client.get_liftover_variation_id( + vrs_id=vrs_variation.id, # type: ignore + starting_assembly=assembly, ) - liftover_variation: Allele | None = anyvar_client.retrieve_allele_by_id( - vrs_id=liftover_vrs_id - ) - if liftover_variation: + + if liftover_vrs_id: + liftover_variation: Allele | None = _validate_allele( + variant=anyvar_client.retrieve_allele_by_id(vrs_id=liftover_vrs_id) + ) + liftover_cafs: list[AnyVlmCohortAlleleFrequencyResult] = ( _retrieve_caf_with_resolved_alleles( variation=liftover_variation, anyvlm_storage=anyvlm_storage ) ) + cafs.extend(liftover_cafs) return cafs diff --git a/src/anyvlm/utils/types.py b/src/anyvlm/utils/types.py index 552991a..cb12c5d 100644 --- a/src/anyvlm/utils/types.py +++ b/src/anyvlm/utils/types.py @@ -70,7 +70,7 @@ class UcscAssemblyBuild(StrEnum): Nucleotide = Annotated[ str, BeforeValidator(str.upper), - StringConstraints(pattern=r"^[ACGT]$"), + StringConstraints(pattern=r"^[ACGT]+$"), ] diff --git a/tests/unit/functions/test_ingest_vcf.py b/tests/unit/functions/test_ingest_vcf.py index dc81e8d..bf68357 100644 --- a/tests/unit/functions/test_ingest_vcf.py +++ b/tests/unit/functions/test_ingest_vcf.py @@ -2,7 +2,7 @@ from pathlib import Path import pytest -from anyvar.core.objects import VrsObject +from anyvar.core.objects import VrsVariation from anyvar.mapping.liftover import ReferenceAssembly from ga4gh.vrs.models import Allele @@ -50,7 +50,7 @@ def retrieve_allele_by_id( self, vrs_id: str, starting_assembly: ReferenceAssembly = ReferenceAssembly.GRCH38, - ) -> VrsObject | None: + ) -> VrsVariation | None: raise NotImplementedError def retrieve_allele_by_expression( From e7a446ec59313aae98b87e526ed1efc61eb03359 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 3 Jun 2026 16:02:39 -0400 Subject: [PATCH 07/21] fix some failing tests --- tests/integration/functions/test_get_caf.py | 25 +++++++++------------ tests/unit/test_variant_counts_endpoint.py | 6 ++--- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/tests/integration/functions/test_get_caf.py b/tests/integration/functions/test_get_caf.py index 29a7008..c372de6 100644 --- a/tests/integration/functions/test_get_caf.py +++ b/tests/integration/functions/test_get_caf.py @@ -5,7 +5,7 @@ from helpers import EXPECTED_VRS_ID, TEST_VARIANT, build_caf from anyvlm.anyvar.python_client import PythonAnyVarClient -from anyvlm.functions.get_caf import VariantNotRegisteredError, get_caf +from anyvlm.functions.get_caf import get_caf from anyvlm.storage.postgres import PostgresObjectStore from anyvlm.utils.types import AnyVlmCohortAlleleFrequencyResult @@ -67,16 +67,13 @@ def test_get_caf_variant_not_registered( populated_postgres_storage: PostgresObjectStore, ): """Test get_caf raises exception due to variant not being registered""" - with pytest.raises( - VariantNotRegisteredError, - match="Variant GRCh38 chrY-2781761-C-A is not registered in AnyVar", - ): - get_caf( - anyvar_minimal_populated_python_client, - populated_postgres_storage, - TEST_VARIANT.assembly, - TEST_VARIANT.chromosome, - TEST_VARIANT.position, - TEST_VARIANT.ref, - TEST_VARIANT.alt, - ) + cafs = get_caf( + anyvar_minimal_populated_python_client, + populated_postgres_storage, + TEST_VARIANT.assembly, + TEST_VARIANT.chromosome, + TEST_VARIANT.position, + TEST_VARIANT.ref, + TEST_VARIANT.alt, + ) + assert cafs == [] diff --git a/tests/unit/test_variant_counts_endpoint.py b/tests/unit/test_variant_counts_endpoint.py index c1af0c8..1ceaa99 100644 --- a/tests/unit/test_variant_counts_endpoint.py +++ b/tests/unit/test_variant_counts_endpoint.py @@ -221,14 +221,14 @@ def test_variant_counts_endpoint_anyvar_unavailable( ): """Test case where AnyVarClientConnectionError is raised""" - def mock_get_registered_allele(*args, **kwargs): + def mock_retrieve_allele_by_id(*args, **kwargs): raise AnyVarClientConnectionError anyvar_client = client_with_populated_dbs.app.state.anyvar_client monkeypatch.setattr( anyvar_client, - "get_registered_allele", - mock_get_registered_allele, + "retrieve_allele_by_id", + mock_retrieve_allele_by_id, ) response = client_with_populated_dbs.get(ENDPOINT, params=TEST_QUERY) From 0e9dcb986d11f734cd7d610979f359309606d291 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 17 Jun 2026 12:20:24 -0400 Subject: [PATCH 08/21] update import to align w/ latest AnyVar variable naming --- pyproject.toml | 2 +- src/anyvlm/anyvar/base_client.py | 4 ++-- src/anyvlm/anyvar/http_client.py | 4 ++-- src/anyvlm/anyvar/python_client.py | 4 ++-- src/anyvlm/functions/get_caf.py | 4 ++-- tests/unit/functions/test_ingest_vcf.py | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ddfcc1c..4f2071c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ classifiers = [ dependencies = [ "ga4gh.vrs>=2.3.0,<3.0", "ga4gh.va_spec~=0.4.3", - "anyvar>=1.0,<2.0", + "anyvar @ git+https://github.com/biocommons/anyvar.git@79cf327aa9f50d4236664d76ad840cc007498549", # Points to the most recent AnyVar commit on `main` as of 06-17-26. TODO: Replace with the latest version of AnyVar once we do the next release "fastapi>=0.95.0", "python-multipart", # required for fastapi file uploads "uvicorn", diff --git a/src/anyvlm/anyvar/base_client.py b/src/anyvlm/anyvar/base_client.py index 3836c50..6ac83e5 100644 --- a/src/anyvlm/anyvar/base_client.py +++ b/src/anyvlm/anyvar/base_client.py @@ -3,7 +3,7 @@ import abc from collections.abc import Iterable, Sequence -from anyvar.core.objects import VrsVariation +from anyvar.core.objects import SupportedVrsVariation from anyvar.mapping.liftover import ReferenceAssembly from ga4gh.vrs.models import Allele @@ -23,7 +23,7 @@ class BaseAnyVarClient(abc.ABC): """Interface elements for an AnyVar client""" @abc.abstractmethod - def retrieve_allele_by_id(self, vrs_id: str) -> VrsVariation | None: + def retrieve_allele_by_id(self, vrs_id: str) -> SupportedVrsVariation | None: """Retrieve VRS Allele for given VRS ID :param vrs_id: The ID to dereference diff --git a/src/anyvlm/anyvar/http_client.py b/src/anyvlm/anyvar/http_client.py index 8442df8..002858e 100644 --- a/src/anyvlm/anyvar/http_client.py +++ b/src/anyvlm/anyvar/http_client.py @@ -7,7 +7,7 @@ import requests from anyvar.core.metadata import VariationMapping -from anyvar.core.objects import VrsVariation +from anyvar.core.objects import SupportedVrsVariation from anyvar.mapping.liftover import ReferenceAssembly from anyvar.restapi.schema import ( GetMappingResponse, @@ -80,7 +80,7 @@ def _make_http_request( raise return response - def retrieve_allele_by_id(self, vrs_id: str) -> VrsVariation | None: + def retrieve_allele_by_id(self, vrs_id: str) -> SupportedVrsVariation | None: """Retrieve VRS Allele for given VRS ID :param vrs_id: The ID to dereference diff --git a/src/anyvlm/anyvar/python_client.py b/src/anyvlm/anyvar/python_client.py index ae8a8a6..706a8cd 100644 --- a/src/anyvlm/anyvar/python_client.py +++ b/src/anyvlm/anyvar/python_client.py @@ -5,7 +5,7 @@ from anyvar import AnyVar from anyvar.core.metadata import VariationMapping, VariationMappingType -from anyvar.core.objects import VrsVariation +from anyvar.core.objects import SupportedVrsVariation from anyvar.mapping.liftover import ReferenceAssembly from anyvar.restapi.schema import SupportedVariationType from anyvar.storage.base import Storage @@ -55,7 +55,7 @@ def _translate_allele_expression( _logger.exception("Failed to translate expression: %s", expression) return translated_variation # type: ignore - def retrieve_allele_by_id(self, vrs_id: str) -> VrsVariation | None: + def retrieve_allele_by_id(self, vrs_id: str) -> SupportedVrsVariation | None: """Retrieve VRS Allele for given VRS ID :param vrs_id: The ID to dereference diff --git a/src/anyvlm/functions/get_caf.py b/src/anyvlm/functions/get_caf.py index e713e6b..af4a568 100644 --- a/src/anyvlm/functions/get_caf.py +++ b/src/anyvlm/functions/get_caf.py @@ -2,7 +2,7 @@ import logging -from anyvar.core.objects import VrsVariation +from anyvar.core.objects import SupportedVrsVariation from ga4gh.core.models import iriReference from ga4gh.vrs.models import Allele @@ -36,7 +36,7 @@ class UnexpectedVariantTypeError(Exception): """Raised when .type is not of the type expected by AnyVLM""" -def _validate_allele(variant: VrsVariation | None) -> Allele: +def _validate_allele(variant: SupportedVrsVariation | None) -> Allele: if not variant: raise VariantLookupError diff --git a/tests/unit/functions/test_ingest_vcf.py b/tests/unit/functions/test_ingest_vcf.py index bf68357..dae30f2 100644 --- a/tests/unit/functions/test_ingest_vcf.py +++ b/tests/unit/functions/test_ingest_vcf.py @@ -2,7 +2,7 @@ from pathlib import Path import pytest -from anyvar.core.objects import VrsVariation +from anyvar.core.objects import SupportedVrsVariation from anyvar.mapping.liftover import ReferenceAssembly from ga4gh.vrs.models import Allele @@ -50,7 +50,7 @@ def retrieve_allele_by_id( self, vrs_id: str, starting_assembly: ReferenceAssembly = ReferenceAssembly.GRCH38, - ) -> VrsVariation | None: + ) -> SupportedVrsVariation | None: raise NotImplementedError def retrieve_allele_by_expression( From e6d7fb1fc9ff12964037142c7384b79f9a3bb2a2 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 17 Jun 2026 12:24:38 -0400 Subject: [PATCH 09/21] update python client's 'get_liftover_variation_id' to use correct syntax for mapping endpoint --- src/anyvlm/anyvar/python_client.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/anyvlm/anyvar/python_client.py b/src/anyvlm/anyvar/python_client.py index 706a8cd..85ea297 100644 --- a/src/anyvlm/anyvar/python_client.py +++ b/src/anyvlm/anyvar/python_client.py @@ -130,12 +130,12 @@ def get_liftover_variation_id( """ as_source: bool = starting_assembly == ReferenceAssembly.GRCH37 liftover_mappings: Iterable[VariationMapping] = self.av.get_object_mappings( - source_object_id=vrs_id, mapping_type=VariationMappingType.LIFTOVER + object_id=vrs_id, + mapping_type=VariationMappingType.LIFTOVER_TO, + as_source=as_source, ) - # liftover_mappings: VariationMapping = self.av.get_object_mappings(source_object_id=vrs_id, mapping_type=VariationMappingType.LIFTOVER, as_source=as_source) - liftover_mapping: VariationMapping | None = next( - iter(liftover_mappings), None - ) # TODO: replace this line with the one above once AnyVar version is updated + liftover_mapping: VariationMapping | None = next(iter(liftover_mappings), None) + return ( (liftover_mapping.dest_id if as_source else liftover_mapping.source_id) if liftover_mapping From 1fbcf8d142d7ef54614ffa94209a8583d82fc7e9 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 17 Jun 2026 12:33:24 -0400 Subject: [PATCH 10/21] fix failing test --- src/anyvlm/functions/get_caf.py | 6 +----- src/anyvlm/restapi/vlm.py | 4 ++-- tests/integration/functions/test_get_caf.py | 24 ++++++++++----------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/anyvlm/functions/get_caf.py b/src/anyvlm/functions/get_caf.py index af4a568..d8b0b67 100644 --- a/src/anyvlm/functions/get_caf.py +++ b/src/anyvlm/functions/get_caf.py @@ -20,10 +20,6 @@ _logger = logging.getLogger(__name__) -class VariantNotRegisteredError(Exception): - """Raised when a variant is not registered in the AnyVar client""" - - class VariantLookupError(Exception): """Raised when a variant cannot be retrieved from AnyVar""" @@ -95,7 +91,7 @@ def get_caf( :param reference_bases: Single genomic base (A/G/C/T) :param alternate_bases: Single genomic base (A/G/C/T) :raises ValueError: if unsupported assembly ID is provided - :raises VariantNotRegisteredError: if variant is not registered in AnyVar + :raises VariantLookupError: if variant is not registered in AnyVar :return: list of AnyVlmCohortAlleleFrequencyResult objects for the provided variant """ gnomad_vcf: str = f"{reference_name}-{start}-{reference_base}-{alternate_base}" diff --git a/src/anyvlm/restapi/vlm.py b/src/anyvlm/restapi/vlm.py index fdb7e4a..37a6136 100644 --- a/src/anyvlm/restapi/vlm.py +++ b/src/anyvlm/restapi/vlm.py @@ -20,7 +20,7 @@ from anyvlm.anyvar.base_client import AnyVarClientConnectionError, BaseAnyVarClient from anyvlm.functions.build_vlm_response import build_vlm_response -from anyvlm.functions.get_caf import VariantNotRegisteredError, get_caf +from anyvlm.functions.get_caf import VariantLookupError, get_caf from anyvlm.functions.ingest_vcf import VcfAfColumnsError from anyvlm.functions.ingest_vcf import ingest_vcf as ingest_vcf_function from anyvlm.schemas.vlm import VlmResponse @@ -322,7 +322,7 @@ def variant_counts( referenceBases, alternateBases, ) - except VariantNotRegisteredError: + except VariantLookupError: caf_data = [] except AnyVarClientConnectionError as e: raise HTTPException( diff --git a/tests/integration/functions/test_get_caf.py b/tests/integration/functions/test_get_caf.py index c372de6..73a99a9 100644 --- a/tests/integration/functions/test_get_caf.py +++ b/tests/integration/functions/test_get_caf.py @@ -5,7 +5,7 @@ from helpers import EXPECTED_VRS_ID, TEST_VARIANT, build_caf from anyvlm.anyvar.python_client import PythonAnyVarClient -from anyvlm.functions.get_caf import get_caf +from anyvlm.functions.get_caf import VariantLookupError, get_caf from anyvlm.storage.postgres import PostgresObjectStore from anyvlm.utils.types import AnyVlmCohortAlleleFrequencyResult @@ -49,7 +49,7 @@ def test_get_caf_no_results_returned( postgres_storage: PostgresObjectStore, ): """Test get_caf when variants are registered but no results are expected""" - cafs = get_caf( + cafs: list[AnyVlmCohortAlleleFrequencyResult] = get_caf( anyvar_populated_python_client, postgres_storage, TEST_VARIANT.assembly, @@ -67,13 +67,13 @@ def test_get_caf_variant_not_registered( populated_postgres_storage: PostgresObjectStore, ): """Test get_caf raises exception due to variant not being registered""" - cafs = get_caf( - anyvar_minimal_populated_python_client, - populated_postgres_storage, - TEST_VARIANT.assembly, - TEST_VARIANT.chromosome, - TEST_VARIANT.position, - TEST_VARIANT.ref, - TEST_VARIANT.alt, - ) - assert cafs == [] + with pytest.raises(VariantLookupError): + get_caf( + anyvar_client=anyvar_minimal_populated_python_client, + anyvlm_storage=populated_postgres_storage, + assembly_id=TEST_VARIANT.assembly, + reference_name=TEST_VARIANT.chromosome, + start=TEST_VARIANT.position, + reference_base=TEST_VARIANT.ref, + alternate_base=TEST_VARIANT.alt, + ) From 3fca09ffa1287e69e3fee7e88062743fa50c1603 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 17 Jun 2026 12:39:36 -0400 Subject: [PATCH 11/21] fix failing test: 'test_variant_counts_endpoint_anyvar_unavailable' --- tests/unit/test_variant_counts_endpoint.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/test_variant_counts_endpoint.py b/tests/unit/test_variant_counts_endpoint.py index 1ceaa99..85a2b7b 100644 --- a/tests/unit/test_variant_counts_endpoint.py +++ b/tests/unit/test_variant_counts_endpoint.py @@ -221,14 +221,14 @@ def test_variant_counts_endpoint_anyvar_unavailable( ): """Test case where AnyVarClientConnectionError is raised""" - def mock_retrieve_allele_by_id(*args, **kwargs): + def retrieve_allele_by_expression(*args, **kwargs): raise AnyVarClientConnectionError anyvar_client = client_with_populated_dbs.app.state.anyvar_client monkeypatch.setattr( anyvar_client, - "retrieve_allele_by_id", - mock_retrieve_allele_by_id, + "retrieve_allele_by_expression", + retrieve_allele_by_expression, ) response = client_with_populated_dbs.get(ENDPOINT, params=TEST_QUERY) From c7604142858d76db14f8d12fe8d128a12c12d5b9 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 17 Jun 2026 14:42:48 -0400 Subject: [PATCH 12/21] fix failing test --- src/anyvlm/anyvar/http_client.py | 4 ++-- ...pulated[anyvar_populated_http_client].yaml | 20 +++++++++---------- ...sions_unpopulated[anyvar_http_client].yaml | 10 +++++----- tests/unit/anyvar/test_clients.py | 4 ++-- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/anyvlm/anyvar/http_client.py b/src/anyvlm/anyvar/http_client.py index 002858e..ff98653 100644 --- a/src/anyvlm/anyvar/http_client.py +++ b/src/anyvlm/anyvar/http_client.py @@ -123,8 +123,8 @@ def retrieve_allele_by_expression( ) return None - validated_response = RegisterVariationResponse(**response.json()) # type ignore - return validated_response.object # type: ignore (input_type=Allele guarantees return type) + validated_response = RegisterVariationResponse(**response.json()) + return validated_response.object def put_allele_expressions( self, diff --git a/tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_populated[anyvar_populated_http_client].yaml b/tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_populated[anyvar_populated_http_client].yaml index c88330c..665c7b3 100644 --- a/tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_populated[anyvar_populated_http_client].yaml +++ b/tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_populated[anyvar_populated_http_client].yaml @@ -68,11 +68,11 @@ interactions: body: '{"definition": "7-140753336-A-T", "assembly_name": "GRCh38", "input_type": "Allele"}' headers: {} - method: POST + method: PUT uri: http://localhost:8000/variation response: body: - string: '{"messages":[],"data":{"id":"ga4gh:VA.Otc5ovrw906Ack087o1fhegB4jDRqCAe","type":"Allele","digest":"Otc5ovrw906Ack087o1fhegB4jDRqCAe","location":{"id":"ga4gh:SL.nhul5x5P_fKjGEpY9PEkMIekJfZaKom2","type":"SequenceLocation","digest":"nhul5x5P_fKjGEpY9PEkMIekJfZaKom2","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul"},"start":140753335,"end":140753336},"state":{"type":"LiteralSequenceExpression","sequence":"T"}}}' + string: '{"messages":[],"input_variation":{"id":"ga4gh:VA.Otc5ovrw906Ack087o1fhegB4jDRqCAe","type":"Allele","digest":"Otc5ovrw906Ack087o1fhegB4jDRqCAe","location":{"id":"ga4gh:SL.nhul5x5P_fKjGEpY9PEkMIekJfZaKom2","type":"SequenceLocation","digest":"nhul5x5P_fKjGEpY9PEkMIekJfZaKom2","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul"},"start":140753335,"end":140753336},"state":{"type":"LiteralSequenceExpression","sequence":"T"}}}' headers: {} status: code: 200 @@ -81,11 +81,11 @@ interactions: body: '{"definition": "Y-2781704-G-G", "assembly_name": "GRCh38", "input_type": "Allele"}' headers: {} - method: POST + method: PUT uri: http://localhost:8000/variation response: body: - string: '{"messages":[],"data":{"id":"ga4gh:VA.0ydMeUtVfU9ttSoziRnp0Nv8OMN359HC","type":"Allele","digest":"0ydMeUtVfU9ttSoziRnp0Nv8OMN359HC","location":{"id":"ga4gh:SL.JqeJ3V-75edWj03xbzw1gtSw3qPQVV2D","type":"SequenceLocation","digest":"JqeJ3V-75edWj03xbzw1gtSw3qPQVV2D","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781703,"end":2781704},"state":{"type":"ReferenceLengthExpression","length":1,"sequence":"G","repeatSubunitLength":1}}}' + string: '{"messages":[],"input_variation":{"id":"ga4gh:VA.0ydMeUtVfU9ttSoziRnp0Nv8OMN359HC","type":"Allele","digest":"0ydMeUtVfU9ttSoziRnp0Nv8OMN359HC","location":{"id":"ga4gh:SL.JqeJ3V-75edWj03xbzw1gtSw3qPQVV2D","type":"SequenceLocation","digest":"JqeJ3V-75edWj03xbzw1gtSw3qPQVV2D","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781703,"end":2781704},"state":{"type":"ReferenceLengthExpression","length":1,"sequence":"G","repeatSubunitLength":1}}}' headers: {} status: code: 200 @@ -94,11 +94,11 @@ interactions: body: '{"definition": "Y-2781761-C-C", "assembly_name": "GRCh38", "input_type": "Allele"}' headers: {} - method: POST + method: PUT uri: http://localhost:8000/variation response: body: - string: '{"messages":[],"data":{"id":"ga4gh:VA.R4kbmdsn5VldGrBiAaByO5N9zM3qCSFw","type":"Allele","digest":"R4kbmdsn5VldGrBiAaByO5N9zM3qCSFw","location":{"id":"ga4gh:SL.sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","type":"SequenceLocation","digest":"sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781760,"end":2781761},"state":{"type":"ReferenceLengthExpression","length":1,"sequence":"C","repeatSubunitLength":1}}}' + string: '{"messages":[],"input_variation":{"id":"ga4gh:VA.R4kbmdsn5VldGrBiAaByO5N9zM3qCSFw","type":"Allele","digest":"R4kbmdsn5VldGrBiAaByO5N9zM3qCSFw","location":{"id":"ga4gh:SL.sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","type":"SequenceLocation","digest":"sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781760,"end":2781761},"state":{"type":"ReferenceLengthExpression","length":1,"sequence":"C","repeatSubunitLength":1}}}' headers: {} status: code: 200 @@ -107,11 +107,11 @@ interactions: body: '{"definition": "Y-2781761-C-A", "assembly_name": "GRCh38", "input_type": "Allele"}' headers: {} - method: POST + method: PUT uri: http://localhost:8000/variation response: body: - string: '{"messages":[],"data":{"id":"ga4gh:VA.9VDxL0stMBOZwcTKw3yb3UoWQkpaI9OD","type":"Allele","digest":"9VDxL0stMBOZwcTKw3yb3UoWQkpaI9OD","location":{"id":"ga4gh:SL.sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","type":"SequenceLocation","digest":"sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781760,"end":2781761},"state":{"type":"LiteralSequenceExpression","sequence":"A"}}}' + string: '{"messages":[],"input_variation":{"id":"ga4gh:VA.9VDxL0stMBOZwcTKw3yb3UoWQkpaI9OD","type":"Allele","digest":"9VDxL0stMBOZwcTKw3yb3UoWQkpaI9OD","location":{"id":"ga4gh:SL.sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","type":"SequenceLocation","digest":"sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781760,"end":2781761},"state":{"type":"LiteralSequenceExpression","sequence":"A"}}}' headers: {} status: code: 200 @@ -120,11 +120,11 @@ interactions: body: '{"definition": "Y-2781761-CA-C", "assembly_name": "GRCh38", "input_type": "Allele"}' headers: {} - method: POST + method: PUT uri: http://localhost:8000/variation response: body: - string: '{"messages":[],"data":{"id":"ga4gh:VA.yi7A2l0uIUMaInQaJnHU_B2Cf_OuZRJg","type":"Allele","digest":"yi7A2l0uIUMaInQaJnHU_B2Cf_OuZRJg","location":{"id":"ga4gh:SL.JsFGLKlUDocinf7oWTXAvVT2WOso7R9u","type":"SequenceLocation","digest":"JsFGLKlUDocinf7oWTXAvVT2WOso7R9u","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781761,"end":2781785},"state":{"type":"ReferenceLengthExpression","length":23,"sequence":"AAAAAAAAAAAAAAAAAAAAAAA","repeatSubunitLength":1}}}' + string: '{"messages":[],"input_variation":{"id":"ga4gh:VA.yi7A2l0uIUMaInQaJnHU_B2Cf_OuZRJg","type":"Allele","digest":"yi7A2l0uIUMaInQaJnHU_B2Cf_OuZRJg","location":{"id":"ga4gh:SL.JsFGLKlUDocinf7oWTXAvVT2WOso7R9u","type":"SequenceLocation","digest":"JsFGLKlUDocinf7oWTXAvVT2WOso7R9u","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781761,"end":2781785},"state":{"type":"ReferenceLengthExpression","length":23,"sequence":"AAAAAAAAAAAAAAAAAAAAAAA","repeatSubunitLength":1}}}' headers: {} status: code: 200 diff --git a/tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_unpopulated[anyvar_http_client].yaml b/tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_unpopulated[anyvar_http_client].yaml index 681e180..d9da21a 100644 --- a/tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_unpopulated[anyvar_http_client].yaml +++ b/tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_unpopulated[anyvar_http_client].yaml @@ -3,7 +3,7 @@ interactions: body: '{"definition": "7-140753336-A-T", "assembly_name": "GRCh38", "input_type": "Allele"}' headers: {} - method: POST + method: PUT uri: http://localhost:8000/variation response: body: @@ -17,7 +17,7 @@ interactions: body: '{"definition": "Y-2781704-G-G", "assembly_name": "GRCh38", "input_type": "Allele"}' headers: {} - method: POST + method: PUT uri: http://localhost:8000/variation response: body: @@ -31,7 +31,7 @@ interactions: body: '{"definition": "Y-2781761-C-C", "assembly_name": "GRCh38", "input_type": "Allele"}' headers: {} - method: POST + method: PUT uri: http://localhost:8000/variation response: body: @@ -45,7 +45,7 @@ interactions: body: '{"definition": "Y-2781761-C-A", "assembly_name": "GRCh38", "input_type": "Allele"}' headers: {} - method: POST + method: PUT uri: http://localhost:8000/variation response: body: @@ -59,7 +59,7 @@ interactions: body: '{"definition": "Y-2781761-CA-C", "assembly_name": "GRCh38", "input_type": "Allele"}' headers: {} - method: POST + method: PUT uri: http://localhost:8000/variation response: body: diff --git a/tests/unit/anyvar/test_clients.py b/tests/unit/anyvar/test_clients.py index d447c0b..07a861f 100644 --- a/tests/unit/anyvar/test_clients.py +++ b/tests/unit/anyvar/test_clients.py @@ -57,7 +57,7 @@ def anyvar_client(request): def test_get_registered_allele_expressions_unpopulated( anyvar_client: BaseAnyVarClient, alleles: dict ): - """Test `get_registered_allele_expressions` for an unpopulated client""" + """Test `retrieve_allele_by_expression` for an unpopulated client""" for allele_fixture in alleles.values(): if "vcf_expression" not in allele_fixture: continue @@ -74,7 +74,7 @@ def test_get_registered_allele_expressions_unpopulated( def test_get_registered_allele_expressions_populated( anyvar_client: BaseAnyVarClient, alleles: dict ): - """Test `get_registered_allele_expressions` for a populated client""" + """Test `retrieve_allele_by_expression` for a populated client""" for allele_fixture in alleles.values(): if "vcf_expression" not in allele_fixture: continue From e20a8a96a1190e96f731f069e00d6b2903403186 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 17 Jun 2026 14:46:11 -0400 Subject: [PATCH 13/21] rename tests + fixtures to reflect new function name --- ...y_expression_populated[anyvar_populated_http_client].yaml} | 0 ...expression_populated[anyvar_populated_python_client].yaml} | 0 ...allele_by_expression_unpopulated[anyvar_http_client].yaml} | 0 ...lele_by_expression_unpopulated[anyvar_python_client].yaml} | 0 tests/unit/anyvar/test_clients.py | 4 ++-- 5 files changed, 2 insertions(+), 2 deletions(-) rename tests/unit/anyvar/cassettes/test_clients/{test_get_registered_allele_expressions_populated[anyvar_populated_http_client].yaml => test_retrieve_allele_by_expression_populated[anyvar_populated_http_client].yaml} (100%) rename tests/unit/anyvar/cassettes/test_clients/{test_get_registered_allele_expressions_populated[anyvar_populated_python_client].yaml => test_retrieve_allele_by_expression_populated[anyvar_populated_python_client].yaml} (100%) rename tests/unit/anyvar/cassettes/test_clients/{test_get_registered_allele_expressions_unpopulated[anyvar_http_client].yaml => test_retrieve_allele_by_expression_unpopulated[anyvar_http_client].yaml} (100%) rename tests/unit/anyvar/cassettes/test_clients/{test_get_registered_allele_expressions_unpopulated[anyvar_python_client].yaml => test_retrieve_allele_by_expression_unpopulated[anyvar_python_client].yaml} (100%) diff --git a/tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_populated[anyvar_populated_http_client].yaml b/tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_http_client].yaml similarity index 100% rename from tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_populated[anyvar_populated_http_client].yaml rename to tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_http_client].yaml diff --git a/tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_populated[anyvar_populated_python_client].yaml b/tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_python_client].yaml similarity index 100% rename from tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_populated[anyvar_populated_python_client].yaml rename to tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_python_client].yaml diff --git a/tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_unpopulated[anyvar_http_client].yaml b/tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_unpopulated[anyvar_http_client].yaml similarity index 100% rename from tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_unpopulated[anyvar_http_client].yaml rename to tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_unpopulated[anyvar_http_client].yaml diff --git a/tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_unpopulated[anyvar_python_client].yaml b/tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_unpopulated[anyvar_python_client].yaml similarity index 100% rename from tests/unit/anyvar/cassettes/test_clients/test_get_registered_allele_expressions_unpopulated[anyvar_python_client].yaml rename to tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_unpopulated[anyvar_python_client].yaml diff --git a/tests/unit/anyvar/test_clients.py b/tests/unit/anyvar/test_clients.py index 07a861f..76d6ca6 100644 --- a/tests/unit/anyvar/test_clients.py +++ b/tests/unit/anyvar/test_clients.py @@ -54,7 +54,7 @@ def anyvar_client(request): @pytest.mark.vcr @pytest.mark.parametrize("anyvar_client", UNPOPULATED_CLIENTS, indirect=True) -def test_get_registered_allele_expressions_unpopulated( +def test_retrieve_allele_by_expression_unpopulated( anyvar_client: BaseAnyVarClient, alleles: dict ): """Test `retrieve_allele_by_expression` for an unpopulated client""" @@ -71,7 +71,7 @@ def test_get_registered_allele_expressions_unpopulated( @pytest.mark.vcr @pytest.mark.parametrize("anyvar_client", POPULATED_CLIENTS, indirect=True) -def test_get_registered_allele_expressions_populated( +def test_retrieve_allele_by_expression_populated( anyvar_client: BaseAnyVarClient, alleles: dict ): """Test `retrieve_allele_by_expression` for a populated client""" From ff9a4c060ea12a33d919f5bdd78a6790946b9050 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 17 Jun 2026 15:30:45 -0400 Subject: [PATCH 14/21] fix cassette --- ...pulated[anyvar_populated_http_client].yaml | 10 +- ...lated[anyvar_populated_python_client].yaml | 542 ------------------ 2 files changed, 5 insertions(+), 547 deletions(-) delete mode 100644 tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_python_client].yaml diff --git a/tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_http_client].yaml b/tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_http_client].yaml index 665c7b3..c2fdfa3 100644 --- a/tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_http_client].yaml +++ b/tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_http_client].yaml @@ -72,7 +72,7 @@ interactions: uri: http://localhost:8000/variation response: body: - string: '{"messages":[],"input_variation":{"id":"ga4gh:VA.Otc5ovrw906Ack087o1fhegB4jDRqCAe","type":"Allele","digest":"Otc5ovrw906Ack087o1fhegB4jDRqCAe","location":{"id":"ga4gh:SL.nhul5x5P_fKjGEpY9PEkMIekJfZaKom2","type":"SequenceLocation","digest":"nhul5x5P_fKjGEpY9PEkMIekJfZaKom2","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul"},"start":140753335,"end":140753336},"state":{"type":"LiteralSequenceExpression","sequence":"T"}}}' + string: '{"input_variation":{"definition":"7-140753336-A-T","input_type":"Allele","assembly_name":"GRCh38"},"messages":[],"object":{"id":"ga4gh:VA.Otc5ovrw906Ack087o1fhegB4jDRqCAe","type":"Allele","digest":"Otc5ovrw906Ack087o1fhegB4jDRqCAe","location":{"id":"ga4gh:SL.nhul5x5P_fKjGEpY9PEkMIekJfZaKom2","type":"SequenceLocation","digest":"nhul5x5P_fKjGEpY9PEkMIekJfZaKom2","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul"},"start":140753335,"end":140753336},"state":{"type":"LiteralSequenceExpression","sequence":"T"}},"object_id":"ga4gh:VA.Otc5ovrw906Ack087o1fhegB4jDRqCAe"}' headers: {} status: code: 200 @@ -85,7 +85,7 @@ interactions: uri: http://localhost:8000/variation response: body: - string: '{"messages":[],"input_variation":{"id":"ga4gh:VA.0ydMeUtVfU9ttSoziRnp0Nv8OMN359HC","type":"Allele","digest":"0ydMeUtVfU9ttSoziRnp0Nv8OMN359HC","location":{"id":"ga4gh:SL.JqeJ3V-75edWj03xbzw1gtSw3qPQVV2D","type":"SequenceLocation","digest":"JqeJ3V-75edWj03xbzw1gtSw3qPQVV2D","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781703,"end":2781704},"state":{"type":"ReferenceLengthExpression","length":1,"sequence":"G","repeatSubunitLength":1}}}' + string: '{"input_variation":{"definition":"Y-2781704-G-G","input_type":"Allele","assembly_name":"GRCh38"},"messages":[],"object":{"id":"ga4gh:VA.0ydMeUtVfU9ttSoziRnp0Nv8OMN359HC","type":"Allele","digest":"0ydMeUtVfU9ttSoziRnp0Nv8OMN359HC","location":{"id":"ga4gh:SL.JqeJ3V-75edWj03xbzw1gtSw3qPQVV2D","type":"SequenceLocation","digest":"JqeJ3V-75edWj03xbzw1gtSw3qPQVV2D","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781703,"end":2781704},"state":{"type":"ReferenceLengthExpression","length":1,"sequence":"G","repeatSubunitLength":1}},"object_id":"ga4gh:VA.0ydMeUtVfU9ttSoziRnp0Nv8OMN359HC"}' headers: {} status: code: 200 @@ -98,7 +98,7 @@ interactions: uri: http://localhost:8000/variation response: body: - string: '{"messages":[],"input_variation":{"id":"ga4gh:VA.R4kbmdsn5VldGrBiAaByO5N9zM3qCSFw","type":"Allele","digest":"R4kbmdsn5VldGrBiAaByO5N9zM3qCSFw","location":{"id":"ga4gh:SL.sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","type":"SequenceLocation","digest":"sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781760,"end":2781761},"state":{"type":"ReferenceLengthExpression","length":1,"sequence":"C","repeatSubunitLength":1}}}' + string: '{"input_variation":{"definition":"Y-2781761-C-C","input_type":"Allele","assembly_name":"GRCh38"},"messages":[],"object":{"id":"ga4gh:VA.R4kbmdsn5VldGrBiAaByO5N9zM3qCSFw","type":"Allele","digest":"R4kbmdsn5VldGrBiAaByO5N9zM3qCSFw","location":{"id":"ga4gh:SL.sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","type":"SequenceLocation","digest":"sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781760,"end":2781761},"state":{"type":"ReferenceLengthExpression","length":1,"sequence":"C","repeatSubunitLength":1}},"object_id":"ga4gh:VA.R4kbmdsn5VldGrBiAaByO5N9zM3qCSFw"}' headers: {} status: code: 200 @@ -111,7 +111,7 @@ interactions: uri: http://localhost:8000/variation response: body: - string: '{"messages":[],"input_variation":{"id":"ga4gh:VA.9VDxL0stMBOZwcTKw3yb3UoWQkpaI9OD","type":"Allele","digest":"9VDxL0stMBOZwcTKw3yb3UoWQkpaI9OD","location":{"id":"ga4gh:SL.sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","type":"SequenceLocation","digest":"sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781760,"end":2781761},"state":{"type":"LiteralSequenceExpression","sequence":"A"}}}' + string: '{"input_variation":{"definition":"Y-2781761-C-A","input_type":"Allele","assembly_name":"GRCh38"},"messages":[],"object":{"id":"ga4gh:VA.9VDxL0stMBOZwcTKw3yb3UoWQkpaI9OD","type":"Allele","digest":"9VDxL0stMBOZwcTKw3yb3UoWQkpaI9OD","location":{"id":"ga4gh:SL.sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","type":"SequenceLocation","digest":"sYiBcbbgF-1CANNCTfQ6zwZOU0iHhymR","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781760,"end":2781761},"state":{"type":"LiteralSequenceExpression","sequence":"A"}},"object_id":"ga4gh:VA.9VDxL0stMBOZwcTKw3yb3UoWQkpaI9OD"}' headers: {} status: code: 200 @@ -124,7 +124,7 @@ interactions: uri: http://localhost:8000/variation response: body: - string: '{"messages":[],"input_variation":{"id":"ga4gh:VA.yi7A2l0uIUMaInQaJnHU_B2Cf_OuZRJg","type":"Allele","digest":"yi7A2l0uIUMaInQaJnHU_B2Cf_OuZRJg","location":{"id":"ga4gh:SL.JsFGLKlUDocinf7oWTXAvVT2WOso7R9u","type":"SequenceLocation","digest":"JsFGLKlUDocinf7oWTXAvVT2WOso7R9u","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781761,"end":2781785},"state":{"type":"ReferenceLengthExpression","length":23,"sequence":"AAAAAAAAAAAAAAAAAAAAAAA","repeatSubunitLength":1}}}' + string: '{"input_variation":{"definition":"Y-2781761-CA-C","input_type":"Allele","assembly_name":"GRCh38"},"messages":[],"object":{"id":"ga4gh:VA.yi7A2l0uIUMaInQaJnHU_B2Cf_OuZRJg","type":"Allele","digest":"yi7A2l0uIUMaInQaJnHU_B2Cf_OuZRJg","location":{"id":"ga4gh:SL.JsFGLKlUDocinf7oWTXAvVT2WOso7R9u","type":"SequenceLocation","digest":"JsFGLKlUDocinf7oWTXAvVT2WOso7R9u","sequenceReference":{"type":"SequenceReference","refgetAccession":"SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5"},"start":2781761,"end":2781785},"state":{"type":"ReferenceLengthExpression","length":23,"sequence":"AAAAAAAAAAAAAAAAAAAAAAA","repeatSubunitLength":1}},"object_id":"ga4gh:VA.yi7A2l0uIUMaInQaJnHU_B2Cf_OuZRJg"}' headers: {} status: code: 200 diff --git a/tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_python_client].yaml b/tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_python_client].yaml deleted file mode 100644 index a1a16cc..0000000 --- a/tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_python_client].yaml +++ /dev/null @@ -1,542 +0,0 @@ -interactions: -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/ping - response: - body: - string: "{\n \"dependencies\": {\n \"bioutils\": {\n \"url\": \"https://github.com/biocommons/bioutils/\",\n - \ \"version\": \"0.5.8.post1\"\n },\n \"seqrepo\": {\n \"root\": - \"/usr/local/share/seqrepo\",\n \"url\": \"https://github.com/biocommons/biocommons.seqrepo/\",\n - \ \"version\": \"0.6.6\"\n }\n },\n \"url\": \"https://github.com/biocommons/seqrepo-rest-service/\",\n - \ \"version\": \"0.2.3.dev0+ge4124b9.d20231114\"\n}\n" - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/metadata/GRCh38:7 - response: - body: - string: "{\n \"added\": \"2016-08-27T21:23:35Z\",\n \"aliases\": [\n \"GRCh38:7\",\n - \ \"GRCh38:chr7\",\n \"GRCh38.p1:7\",\n \"GRCh38.p1:chr7\",\n \"GRCh38.p10:7\",\n - \ \"GRCh38.p10:chr7\",\n \"GRCh38.p11:7\",\n \"GRCh38.p11:chr7\",\n - \ \"GRCh38.p12:7\",\n \"GRCh38.p12:chr7\",\n \"GRCh38.p2:7\",\n \"GRCh38.p2:chr7\",\n - \ \"GRCh38.p3:7\",\n \"GRCh38.p3:chr7\",\n \"GRCh38.p4:7\",\n \"GRCh38.p4:chr7\",\n - \ \"GRCh38.p5:7\",\n \"GRCh38.p5:chr7\",\n \"GRCh38.p6:7\",\n \"GRCh38.p6:chr7\",\n - \ \"GRCh38.p7:7\",\n \"GRCh38.p7:chr7\",\n \"GRCh38.p8:7\",\n \"GRCh38.p8:chr7\",\n - \ \"GRCh38.p9:7\",\n \"GRCh38.p9:chr7\",\n \"MD5:cc044cc2256a1141212660fb07b6171e\",\n - \ \"NCBI:NC_000007.14\",\n \"refseq:NC_000007.14\",\n \"SEGUID:4+JjCcBVhPCr8vdIhUKFycPv8bY\",\n - \ \"SHA1:e3e26309c05584f0abf2f748854285c9c3eff1b6\",\n \"VMC:GS_F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul\",\n - \ \"sha512t24u:F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul\",\n \"ga4gh:SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul\"\n - \ ],\n \"alphabet\": \"ACGNRSTY\",\n \"length\": 159345973\n}\n" - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/GRCh38:7?start=140753335&end=140753336 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/metadata/ga4gh:SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul - response: - body: - string: "{\n \"added\": \"2016-08-27T21:23:35Z\",\n \"aliases\": [\n \"GRCh38:7\",\n - \ \"GRCh38:chr7\",\n \"GRCh38.p1:7\",\n \"GRCh38.p1:chr7\",\n \"GRCh38.p10:7\",\n - \ \"GRCh38.p10:chr7\",\n \"GRCh38.p11:7\",\n \"GRCh38.p11:chr7\",\n - \ \"GRCh38.p12:7\",\n \"GRCh38.p12:chr7\",\n \"GRCh38.p2:7\",\n \"GRCh38.p2:chr7\",\n - \ \"GRCh38.p3:7\",\n \"GRCh38.p3:chr7\",\n \"GRCh38.p4:7\",\n \"GRCh38.p4:chr7\",\n - \ \"GRCh38.p5:7\",\n \"GRCh38.p5:chr7\",\n \"GRCh38.p6:7\",\n \"GRCh38.p6:chr7\",\n - \ \"GRCh38.p7:7\",\n \"GRCh38.p7:chr7\",\n \"GRCh38.p8:7\",\n \"GRCh38.p8:chr7\",\n - \ \"GRCh38.p9:7\",\n \"GRCh38.p9:chr7\",\n \"MD5:cc044cc2256a1141212660fb07b6171e\",\n - \ \"NCBI:NC_000007.14\",\n \"refseq:NC_000007.14\",\n \"SEGUID:4+JjCcBVhPCr8vdIhUKFycPv8bY\",\n - \ \"SHA1:e3e26309c05584f0abf2f748854285c9c3eff1b6\",\n \"VMC:GS_F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul\",\n - \ \"sha512t24u:F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul\",\n \"ga4gh:SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul\"\n - \ ],\n \"alphabet\": \"ACGNRSTY\",\n \"length\": 159345973\n}\n" - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul?start=140753335&end=140753336 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/metadata/GRCh38:Y - response: - body: - string: "{\n \"added\": \"2016-08-27T23:57:42Z\",\n \"aliases\": [\n \"GRCh38:Y\",\n - \ \"GRCh38:chrY\",\n \"GRCh38.p1:Y\",\n \"GRCh38.p1:chrY\",\n \"GRCh38.p10:Y\",\n - \ \"GRCh38.p10:chrY\",\n \"GRCh38.p11:Y\",\n \"GRCh38.p11:chrY\",\n - \ \"GRCh38.p12:Y\",\n \"GRCh38.p12:chrY\",\n \"GRCh38.p2:Y\",\n \"GRCh38.p2:chrY\",\n - \ \"GRCh38.p3:Y\",\n \"GRCh38.p3:chrY\",\n \"GRCh38.p4:Y\",\n \"GRCh38.p4:chrY\",\n - \ \"GRCh38.p5:Y\",\n \"GRCh38.p5:chrY\",\n \"GRCh38.p6:Y\",\n \"GRCh38.p6:chrY\",\n - \ \"GRCh38.p7:Y\",\n \"GRCh38.p7:chrY\",\n \"GRCh38.p8:Y\",\n \"GRCh38.p8:chrY\",\n - \ \"GRCh38.p9:Y\",\n \"GRCh38.p9:chrY\",\n \"MD5:447f54a94ef525e42ce58d3e0c48b3f8\",\n - \ \"NCBI:NC_000024.10\",\n \"refseq:NC_000024.10\",\n \"SEGUID:Aa5HItLyfT0VRdz6r0SjqaidoCQ\",\n - \ \"SHA1:01ae4722d2f27d3d1545dcfaaf44a3a9a89da024\",\n \"VMC:GS_8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5\",\n - \ \"sha512t24u:8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5\",\n \"ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5\"\n - \ ],\n \"alphabet\": \"ACGNRSTWY\",\n \"length\": 57227415\n}\n" - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/GRCh38:Y?start=2781703&end=2781704 - response: - body: - string: G - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/metadata/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5 - response: - body: - string: "{\n \"added\": \"2016-08-27T23:57:42Z\",\n \"aliases\": [\n \"GRCh38:Y\",\n - \ \"GRCh38:chrY\",\n \"GRCh38.p1:Y\",\n \"GRCh38.p1:chrY\",\n \"GRCh38.p10:Y\",\n - \ \"GRCh38.p10:chrY\",\n \"GRCh38.p11:Y\",\n \"GRCh38.p11:chrY\",\n - \ \"GRCh38.p12:Y\",\n \"GRCh38.p12:chrY\",\n \"GRCh38.p2:Y\",\n \"GRCh38.p2:chrY\",\n - \ \"GRCh38.p3:Y\",\n \"GRCh38.p3:chrY\",\n \"GRCh38.p4:Y\",\n \"GRCh38.p4:chrY\",\n - \ \"GRCh38.p5:Y\",\n \"GRCh38.p5:chrY\",\n \"GRCh38.p6:Y\",\n \"GRCh38.p6:chrY\",\n - \ \"GRCh38.p7:Y\",\n \"GRCh38.p7:chrY\",\n \"GRCh38.p8:Y\",\n \"GRCh38.p8:chrY\",\n - \ \"GRCh38.p9:Y\",\n \"GRCh38.p9:chrY\",\n \"MD5:447f54a94ef525e42ce58d3e0c48b3f8\",\n - \ \"NCBI:NC_000024.10\",\n \"refseq:NC_000024.10\",\n \"SEGUID:Aa5HItLyfT0VRdz6r0SjqaidoCQ\",\n - \ \"SHA1:01ae4722d2f27d3d1545dcfaaf44a3a9a89da024\",\n \"VMC:GS_8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5\",\n - \ \"sha512t24u:8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5\",\n \"ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5\"\n - \ ],\n \"alphabet\": \"ACGNRSTWY\",\n \"length\": 57227415\n}\n" - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781703&end=2781704 - response: - body: - string: G - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/GRCh38:Y?start=2781760&end=2781761 - response: - body: - string: C - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781760&end=2781761 - response: - body: - string: C - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/GRCh38:Y?start=2781760&end=2781762 - response: - body: - string: CA - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781760&end=2781762 - response: - body: - string: CA - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781761&end=2781762 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781762&end=2781763 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781763&end=2781764 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781764&end=2781765 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781765&end=2781766 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781766&end=2781767 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781767&end=2781768 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781768&end=2781769 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781769&end=2781770 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781770&end=2781771 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781771&end=2781772 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781772&end=2781773 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781773&end=2781774 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781774&end=2781775 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781775&end=2781776 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781776&end=2781777 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781777&end=2781778 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781778&end=2781779 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781779&end=2781780 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781780&end=2781781 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781781&end=2781782 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781782&end=2781783 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781783&end=2781784 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781784&end=2781785 - response: - body: - string: A - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781785&end=2781786 - response: - body: - string: G - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781761&end=2781761 - response: - body: - string: '' - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781762&end=2781785 - response: - body: - string: AAAAAAAAAAAAAAAAAAAAAAA - headers: {} - status: - code: 200 - message: OK -- request: - body: null - headers: {} - method: GET - uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781761&end=2781785 - response: - body: - string: AAAAAAAAAAAAAAAAAAAAAAAA - headers: {} - status: - code: 200 - message: OK -version: 1 From 4ea007995b0ecb025cc337b1b323517d48e98ed8 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 17 Jun 2026 15:44:53 -0400 Subject: [PATCH 15/21] bring back accidentally nuked cassette + rename it --- ...lated[anyvar_populated_python_client].yaml | 542 ++++++++++++++++++ 1 file changed, 542 insertions(+) create mode 100644 tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_python_client].yaml diff --git a/tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_python_client].yaml b/tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_python_client].yaml new file mode 100644 index 0000000..a1a16cc --- /dev/null +++ b/tests/unit/anyvar/cassettes/test_clients/test_retrieve_allele_by_expression_populated[anyvar_populated_python_client].yaml @@ -0,0 +1,542 @@ +interactions: +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/ping + response: + body: + string: "{\n \"dependencies\": {\n \"bioutils\": {\n \"url\": \"https://github.com/biocommons/bioutils/\",\n + \ \"version\": \"0.5.8.post1\"\n },\n \"seqrepo\": {\n \"root\": + \"/usr/local/share/seqrepo\",\n \"url\": \"https://github.com/biocommons/biocommons.seqrepo/\",\n + \ \"version\": \"0.6.6\"\n }\n },\n \"url\": \"https://github.com/biocommons/seqrepo-rest-service/\",\n + \ \"version\": \"0.2.3.dev0+ge4124b9.d20231114\"\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/metadata/GRCh38:7 + response: + body: + string: "{\n \"added\": \"2016-08-27T21:23:35Z\",\n \"aliases\": [\n \"GRCh38:7\",\n + \ \"GRCh38:chr7\",\n \"GRCh38.p1:7\",\n \"GRCh38.p1:chr7\",\n \"GRCh38.p10:7\",\n + \ \"GRCh38.p10:chr7\",\n \"GRCh38.p11:7\",\n \"GRCh38.p11:chr7\",\n + \ \"GRCh38.p12:7\",\n \"GRCh38.p12:chr7\",\n \"GRCh38.p2:7\",\n \"GRCh38.p2:chr7\",\n + \ \"GRCh38.p3:7\",\n \"GRCh38.p3:chr7\",\n \"GRCh38.p4:7\",\n \"GRCh38.p4:chr7\",\n + \ \"GRCh38.p5:7\",\n \"GRCh38.p5:chr7\",\n \"GRCh38.p6:7\",\n \"GRCh38.p6:chr7\",\n + \ \"GRCh38.p7:7\",\n \"GRCh38.p7:chr7\",\n \"GRCh38.p8:7\",\n \"GRCh38.p8:chr7\",\n + \ \"GRCh38.p9:7\",\n \"GRCh38.p9:chr7\",\n \"MD5:cc044cc2256a1141212660fb07b6171e\",\n + \ \"NCBI:NC_000007.14\",\n \"refseq:NC_000007.14\",\n \"SEGUID:4+JjCcBVhPCr8vdIhUKFycPv8bY\",\n + \ \"SHA1:e3e26309c05584f0abf2f748854285c9c3eff1b6\",\n \"VMC:GS_F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul\",\n + \ \"sha512t24u:F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul\",\n \"ga4gh:SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul\"\n + \ ],\n \"alphabet\": \"ACGNRSTY\",\n \"length\": 159345973\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/GRCh38:7?start=140753335&end=140753336 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/metadata/ga4gh:SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul + response: + body: + string: "{\n \"added\": \"2016-08-27T21:23:35Z\",\n \"aliases\": [\n \"GRCh38:7\",\n + \ \"GRCh38:chr7\",\n \"GRCh38.p1:7\",\n \"GRCh38.p1:chr7\",\n \"GRCh38.p10:7\",\n + \ \"GRCh38.p10:chr7\",\n \"GRCh38.p11:7\",\n \"GRCh38.p11:chr7\",\n + \ \"GRCh38.p12:7\",\n \"GRCh38.p12:chr7\",\n \"GRCh38.p2:7\",\n \"GRCh38.p2:chr7\",\n + \ \"GRCh38.p3:7\",\n \"GRCh38.p3:chr7\",\n \"GRCh38.p4:7\",\n \"GRCh38.p4:chr7\",\n + \ \"GRCh38.p5:7\",\n \"GRCh38.p5:chr7\",\n \"GRCh38.p6:7\",\n \"GRCh38.p6:chr7\",\n + \ \"GRCh38.p7:7\",\n \"GRCh38.p7:chr7\",\n \"GRCh38.p8:7\",\n \"GRCh38.p8:chr7\",\n + \ \"GRCh38.p9:7\",\n \"GRCh38.p9:chr7\",\n \"MD5:cc044cc2256a1141212660fb07b6171e\",\n + \ \"NCBI:NC_000007.14\",\n \"refseq:NC_000007.14\",\n \"SEGUID:4+JjCcBVhPCr8vdIhUKFycPv8bY\",\n + \ \"SHA1:e3e26309c05584f0abf2f748854285c9c3eff1b6\",\n \"VMC:GS_F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul\",\n + \ \"sha512t24u:F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul\",\n \"ga4gh:SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul\"\n + \ ],\n \"alphabet\": \"ACGNRSTY\",\n \"length\": 159345973\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul?start=140753335&end=140753336 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/metadata/GRCh38:Y + response: + body: + string: "{\n \"added\": \"2016-08-27T23:57:42Z\",\n \"aliases\": [\n \"GRCh38:Y\",\n + \ \"GRCh38:chrY\",\n \"GRCh38.p1:Y\",\n \"GRCh38.p1:chrY\",\n \"GRCh38.p10:Y\",\n + \ \"GRCh38.p10:chrY\",\n \"GRCh38.p11:Y\",\n \"GRCh38.p11:chrY\",\n + \ \"GRCh38.p12:Y\",\n \"GRCh38.p12:chrY\",\n \"GRCh38.p2:Y\",\n \"GRCh38.p2:chrY\",\n + \ \"GRCh38.p3:Y\",\n \"GRCh38.p3:chrY\",\n \"GRCh38.p4:Y\",\n \"GRCh38.p4:chrY\",\n + \ \"GRCh38.p5:Y\",\n \"GRCh38.p5:chrY\",\n \"GRCh38.p6:Y\",\n \"GRCh38.p6:chrY\",\n + \ \"GRCh38.p7:Y\",\n \"GRCh38.p7:chrY\",\n \"GRCh38.p8:Y\",\n \"GRCh38.p8:chrY\",\n + \ \"GRCh38.p9:Y\",\n \"GRCh38.p9:chrY\",\n \"MD5:447f54a94ef525e42ce58d3e0c48b3f8\",\n + \ \"NCBI:NC_000024.10\",\n \"refseq:NC_000024.10\",\n \"SEGUID:Aa5HItLyfT0VRdz6r0SjqaidoCQ\",\n + \ \"SHA1:01ae4722d2f27d3d1545dcfaaf44a3a9a89da024\",\n \"VMC:GS_8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5\",\n + \ \"sha512t24u:8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5\",\n \"ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5\"\n + \ ],\n \"alphabet\": \"ACGNRSTWY\",\n \"length\": 57227415\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/GRCh38:Y?start=2781703&end=2781704 + response: + body: + string: G + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/metadata/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5 + response: + body: + string: "{\n \"added\": \"2016-08-27T23:57:42Z\",\n \"aliases\": [\n \"GRCh38:Y\",\n + \ \"GRCh38:chrY\",\n \"GRCh38.p1:Y\",\n \"GRCh38.p1:chrY\",\n \"GRCh38.p10:Y\",\n + \ \"GRCh38.p10:chrY\",\n \"GRCh38.p11:Y\",\n \"GRCh38.p11:chrY\",\n + \ \"GRCh38.p12:Y\",\n \"GRCh38.p12:chrY\",\n \"GRCh38.p2:Y\",\n \"GRCh38.p2:chrY\",\n + \ \"GRCh38.p3:Y\",\n \"GRCh38.p3:chrY\",\n \"GRCh38.p4:Y\",\n \"GRCh38.p4:chrY\",\n + \ \"GRCh38.p5:Y\",\n \"GRCh38.p5:chrY\",\n \"GRCh38.p6:Y\",\n \"GRCh38.p6:chrY\",\n + \ \"GRCh38.p7:Y\",\n \"GRCh38.p7:chrY\",\n \"GRCh38.p8:Y\",\n \"GRCh38.p8:chrY\",\n + \ \"GRCh38.p9:Y\",\n \"GRCh38.p9:chrY\",\n \"MD5:447f54a94ef525e42ce58d3e0c48b3f8\",\n + \ \"NCBI:NC_000024.10\",\n \"refseq:NC_000024.10\",\n \"SEGUID:Aa5HItLyfT0VRdz6r0SjqaidoCQ\",\n + \ \"SHA1:01ae4722d2f27d3d1545dcfaaf44a3a9a89da024\",\n \"VMC:GS_8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5\",\n + \ \"sha512t24u:8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5\",\n \"ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5\"\n + \ ],\n \"alphabet\": \"ACGNRSTWY\",\n \"length\": 57227415\n}\n" + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781703&end=2781704 + response: + body: + string: G + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/GRCh38:Y?start=2781760&end=2781761 + response: + body: + string: C + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781760&end=2781761 + response: + body: + string: C + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/GRCh38:Y?start=2781760&end=2781762 + response: + body: + string: CA + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781760&end=2781762 + response: + body: + string: CA + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781761&end=2781762 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781762&end=2781763 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781763&end=2781764 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781764&end=2781765 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781765&end=2781766 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781766&end=2781767 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781767&end=2781768 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781768&end=2781769 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781769&end=2781770 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781770&end=2781771 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781771&end=2781772 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781772&end=2781773 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781773&end=2781774 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781774&end=2781775 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781775&end=2781776 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781776&end=2781777 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781777&end=2781778 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781778&end=2781779 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781779&end=2781780 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781780&end=2781781 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781781&end=2781782 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781782&end=2781783 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781783&end=2781784 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781784&end=2781785 + response: + body: + string: A + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781785&end=2781786 + response: + body: + string: G + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781761&end=2781761 + response: + body: + string: '' + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781762&end=2781785 + response: + body: + string: AAAAAAAAAAAAAAAAAAAAAAA + headers: {} + status: + code: 200 + message: OK +- request: + body: null + headers: {} + method: GET + uri: http://localhost:5000/seqrepo/1/sequence/ga4gh:SQ.8_liLu1aycC0tPQPFmUaGXJLDs5SbPZ5?start=2781761&end=2781785 + response: + body: + string: AAAAAAAAAAAAAAAAAAAAAAAA + headers: {} + status: + code: 200 + message: OK +version: 1 From d5aa3752612d2e99715d970099cb6b5668742bb1 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 24 Jun 2026 09:03:09 -0400 Subject: [PATCH 16/21] revert change to 'Nucleotide' that would've allowed multiple Nucleotides instead of restraining it to one --- src/anyvlm/utils/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/anyvlm/utils/types.py b/src/anyvlm/utils/types.py index cb12c5d..552991a 100644 --- a/src/anyvlm/utils/types.py +++ b/src/anyvlm/utils/types.py @@ -70,7 +70,7 @@ class UcscAssemblyBuild(StrEnum): Nucleotide = Annotated[ str, BeforeValidator(str.upper), - StringConstraints(pattern=r"^[ACGT]+$"), + StringConstraints(pattern=r"^[ACGT]$"), ] From b46ea3de7321aff905713dba96a998a3fa799544 Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 24 Jun 2026 09:34:23 -0400 Subject: [PATCH 17/21] use enum instead of text matching --- src/anyvlm/functions/get_caf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/anyvlm/functions/get_caf.py b/src/anyvlm/functions/get_caf.py index d8b0b67..5e91d2a 100644 --- a/src/anyvlm/functions/get_caf.py +++ b/src/anyvlm/functions/get_caf.py @@ -4,7 +4,7 @@ from anyvar.core.objects import SupportedVrsVariation from ga4gh.core.models import iriReference -from ga4gh.vrs.models import Allele +from ga4gh.vrs.models import Allele, VrsType from anyvlm.anyvar.base_client import BaseAnyVarClient from anyvlm.storage.base_storage import Storage @@ -40,7 +40,7 @@ def _validate_allele(variant: SupportedVrsVariation | None) -> Allele: if not variant_id: raise IncompleteVariantError - if not variant.type == "Allele": + if not variant.type == str(VrsType.ALLELE): raise UnexpectedVariantTypeError return variant From 5f36d3a44f7315def68070a30049bcbf6c5f2a5a Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 24 Jun 2026 09:46:33 -0400 Subject: [PATCH 18/21] rename 'get_caf_*' to 'get_cafs' + fix '_validate_allele' logic --- .../functions/{get_caf.py => get_cafs.py} | 21 +++++++++--------- src/anyvlm/restapi/vlm.py | 4 ++-- src/anyvlm/storage/base_storage.py | 2 +- src/anyvlm/storage/postgres.py | 2 +- ...=> test_get_cafs_no_results_returned.yaml} | 0 ...ml => test_get_cafs_results_returned.yaml} | 0 ...test_get_cafs_variant_not_registered.yaml} | 0 .../{test_get_caf.py => test_get_cafs.py} | 22 +++++++++---------- 8 files changed, 26 insertions(+), 25 deletions(-) rename src/anyvlm/functions/{get_caf.py => get_cafs.py} (88%) rename tests/integration/functions/cassettes/test_get_caf/{test_get_caf_no_results_returned.yaml => test_get_cafs_no_results_returned.yaml} (100%) rename tests/integration/functions/cassettes/test_get_caf/{test_get_caf_results_returned.yaml => test_get_cafs_results_returned.yaml} (100%) rename tests/integration/functions/cassettes/test_get_caf/{test_get_caf_variant_not_registered.yaml => test_get_cafs_variant_not_registered.yaml} (100%) rename tests/integration/functions/{test_get_caf.py => test_get_cafs.py} (78%) diff --git a/src/anyvlm/functions/get_caf.py b/src/anyvlm/functions/get_cafs.py similarity index 88% rename from src/anyvlm/functions/get_caf.py rename to src/anyvlm/functions/get_cafs.py index 5e91d2a..2079188 100644 --- a/src/anyvlm/functions/get_caf.py +++ b/src/anyvlm/functions/get_cafs.py @@ -36,17 +36,16 @@ def _validate_allele(variant: SupportedVrsVariation | None) -> Allele: if not variant: raise VariantLookupError - variant_id: str | None = variant.id - if not variant_id: + if not variant.id: raise IncompleteVariantError - if not variant.type == str(VrsType.ALLELE): + if not variant.type == str(VrsType.ALLELE.value): raise UnexpectedVariantTypeError return variant -def _retrieve_caf_with_resolved_alleles( +def _retrieve_cafs_with_resolved_alleles( variation: Allele, anyvlm_storage: Storage ) -> list[AnyVlmCohortAlleleFrequencyResult]: """Retrieve CAF data for a resolved allele. @@ -60,7 +59,7 @@ def _retrieve_caf_with_resolved_alleles( raise IncompleteVariantError(error_message) cafs: list[AnyVlmCohortAlleleFrequencyResult] = ( - anyvlm_storage.get_caf_by_vrs_allele_id(vrs_allele_id=variation.id) + anyvlm_storage.get_cafs_by_vrs_allele_id(vrs_allele_id=variation.id) ) for caf in cafs: @@ -70,7 +69,7 @@ def _retrieve_caf_with_resolved_alleles( return cafs -def get_caf( +def get_cafs( anyvar_client: BaseAnyVarClient, anyvlm_storage: Storage, assembly_id: GrcAssemblyId | UcscAssemblyBuild, @@ -105,8 +104,10 @@ def get_caf( variant=anyvar_client.retrieve_allele_by_expression(gnomad_vcf, assembly) ) - cafs: list[AnyVlmCohortAlleleFrequencyResult] = _retrieve_caf_with_resolved_alleles( - variation=vrs_variation, anyvlm_storage=anyvlm_storage + cafs: list[AnyVlmCohortAlleleFrequencyResult] = ( + _retrieve_cafs_with_resolved_alleles( + variation=vrs_variation, anyvlm_storage=anyvlm_storage + ) ) liftover_vrs_id: str | None = anyvar_client.get_liftover_variation_id( @@ -115,12 +116,12 @@ def get_caf( ) if liftover_vrs_id: - liftover_variation: Allele | None = _validate_allele( + liftover_variation: Allele = _validate_allele( variant=anyvar_client.retrieve_allele_by_id(vrs_id=liftover_vrs_id) ) liftover_cafs: list[AnyVlmCohortAlleleFrequencyResult] = ( - _retrieve_caf_with_resolved_alleles( + _retrieve_cafs_with_resolved_alleles( variation=liftover_variation, anyvlm_storage=anyvlm_storage ) ) diff --git a/src/anyvlm/restapi/vlm.py b/src/anyvlm/restapi/vlm.py index 37a6136..4bd1db5 100644 --- a/src/anyvlm/restapi/vlm.py +++ b/src/anyvlm/restapi/vlm.py @@ -20,7 +20,7 @@ from anyvlm.anyvar.base_client import AnyVarClientConnectionError, BaseAnyVarClient from anyvlm.functions.build_vlm_response import build_vlm_response -from anyvlm.functions.get_caf import VariantLookupError, get_caf +from anyvlm.functions.get_cafs import VariantLookupError, get_cafs from anyvlm.functions.ingest_vcf import VcfAfColumnsError from anyvlm.functions.ingest_vcf import ingest_vcf as ingest_vcf_function from anyvlm.schemas.vlm import VlmResponse @@ -313,7 +313,7 @@ def variant_counts( anyvlm_storage: Storage = request.app.state.anyvlm_storage try: - caf_data: list[AnyVlmCohortAlleleFrequencyResult] = get_caf( + caf_data: list[AnyVlmCohortAlleleFrequencyResult] = get_cafs( anyvar_client, anyvlm_storage, assemblyId, diff --git a/src/anyvlm/storage/base_storage.py b/src/anyvlm/storage/base_storage.py index 3d78196..c31a262 100644 --- a/src/anyvlm/storage/base_storage.py +++ b/src/anyvlm/storage/base_storage.py @@ -39,7 +39,7 @@ def add_allele_frequencies( """ @abstractmethod - def get_caf_by_vrs_allele_id( + def get_cafs_by_vrs_allele_id( self, vrs_allele_id: str ) -> list[AnyVlmCohortAlleleFrequencyResult]: """Retrieve cohort allele frequency study results by VRS Allele ID diff --git a/src/anyvlm/storage/postgres.py b/src/anyvlm/storage/postgres.py index 07d9544..0be1571 100644 --- a/src/anyvlm/storage/postgres.py +++ b/src/anyvlm/storage/postgres.py @@ -70,7 +70,7 @@ def add_allele_frequencies( with self.session_factory() as session, session.begin(): session.execute(stmt, [entity.to_dict() for entity in db_entities]) - def get_caf_by_vrs_allele_id( + def get_cafs_by_vrs_allele_id( self, vrs_allele_id: str ) -> list[AnyVlmCohortAlleleFrequencyResult]: """Retrieve cohort allele frequency study results by VRS Allele ID diff --git a/tests/integration/functions/cassettes/test_get_caf/test_get_caf_no_results_returned.yaml b/tests/integration/functions/cassettes/test_get_caf/test_get_cafs_no_results_returned.yaml similarity index 100% rename from tests/integration/functions/cassettes/test_get_caf/test_get_caf_no_results_returned.yaml rename to tests/integration/functions/cassettes/test_get_caf/test_get_cafs_no_results_returned.yaml diff --git a/tests/integration/functions/cassettes/test_get_caf/test_get_caf_results_returned.yaml b/tests/integration/functions/cassettes/test_get_caf/test_get_cafs_results_returned.yaml similarity index 100% rename from tests/integration/functions/cassettes/test_get_caf/test_get_caf_results_returned.yaml rename to tests/integration/functions/cassettes/test_get_caf/test_get_cafs_results_returned.yaml diff --git a/tests/integration/functions/cassettes/test_get_caf/test_get_caf_variant_not_registered.yaml b/tests/integration/functions/cassettes/test_get_caf/test_get_cafs_variant_not_registered.yaml similarity index 100% rename from tests/integration/functions/cassettes/test_get_caf/test_get_caf_variant_not_registered.yaml rename to tests/integration/functions/cassettes/test_get_caf/test_get_cafs_variant_not_registered.yaml diff --git a/tests/integration/functions/test_get_caf.py b/tests/integration/functions/test_get_cafs.py similarity index 78% rename from tests/integration/functions/test_get_caf.py rename to tests/integration/functions/test_get_cafs.py index 73a99a9..e915e6d 100644 --- a/tests/integration/functions/test_get_caf.py +++ b/tests/integration/functions/test_get_cafs.py @@ -1,11 +1,11 @@ -"""Test that get_caf function works correctly""" +"""Test that get_cafs function works correctly""" import pytest from deepdiff import DeepDiff from helpers import EXPECTED_VRS_ID, TEST_VARIANT, build_caf from anyvlm.anyvar.python_client import PythonAnyVarClient -from anyvlm.functions.get_caf import VariantLookupError, get_caf +from anyvlm.functions.get_cafs import VariantLookupError, get_cafs from anyvlm.storage.postgres import PostgresObjectStore from anyvlm.utils.types import AnyVlmCohortAlleleFrequencyResult @@ -20,13 +20,13 @@ def expected_cafs(caf_iri: AnyVlmCohortAlleleFrequencyResult, alleles: dict): @pytest.mark.vcr -def test_get_caf_results_returned( +def test_get_cafs_results_returned( anyvar_populated_python_client: PythonAnyVarClient, populated_postgres_storage: PostgresObjectStore, expected_cafs: list[AnyVlmCohortAlleleFrequencyResult], ): - """Test get_caf when variants are registered and results are expected""" - cafs = get_caf( + """Test get_cafs when variants are registered and results are expected""" + cafs = get_cafs( anyvar_populated_python_client, populated_postgres_storage, TEST_VARIANT.assembly, @@ -44,12 +44,12 @@ def test_get_caf_results_returned( @pytest.mark.vcr -def test_get_caf_no_results_returned( +def test_get_cafs_no_results_returned( anyvar_populated_python_client: PythonAnyVarClient, postgres_storage: PostgresObjectStore, ): - """Test get_caf when variants are registered but no results are expected""" - cafs: list[AnyVlmCohortAlleleFrequencyResult] = get_caf( + """Test get_cafs when variants are registered but no results are expected""" + cafs: list[AnyVlmCohortAlleleFrequencyResult] = get_cafs( anyvar_populated_python_client, postgres_storage, TEST_VARIANT.assembly, @@ -62,13 +62,13 @@ def test_get_caf_no_results_returned( @pytest.mark.vcr -def test_get_caf_variant_not_registered( +def test_get_cafs_variant_not_registered( anyvar_minimal_populated_python_client: PythonAnyVarClient, populated_postgres_storage: PostgresObjectStore, ): - """Test get_caf raises exception due to variant not being registered""" + """Test get_cafs raises exception due to variant not being registered""" with pytest.raises(VariantLookupError): - get_caf( + get_cafs( anyvar_client=anyvar_minimal_populated_python_client, anyvlm_storage=populated_postgres_storage, assembly_id=TEST_VARIANT.assembly, From 7104877d71adba173bbb1bb9f1c45dd9c26d508d Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 24 Jun 2026 11:00:45 -0400 Subject: [PATCH 19/21] remove id check in '_retrieve_cafs_with_resolved_alleles', since '_validate_allele' already does that --- src/anyvlm/functions/get_cafs.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/anyvlm/functions/get_cafs.py b/src/anyvlm/functions/get_cafs.py index 2079188..6cd9ab5 100644 --- a/src/anyvlm/functions/get_cafs.py +++ b/src/anyvlm/functions/get_cafs.py @@ -54,12 +54,8 @@ def _retrieve_cafs_with_resolved_alleles( :param anyvlm_storage: The storage for this AnyVLM instance :return: A list of AnyVlmCohortAlleleFrequencyResult objects """ - if not variation.id: - error_message: str = "Variants must include an 'id'" - raise IncompleteVariantError(error_message) - cafs: list[AnyVlmCohortAlleleFrequencyResult] = ( - anyvlm_storage.get_cafs_by_vrs_allele_id(vrs_allele_id=variation.id) + anyvlm_storage.get_cafs_by_vrs_allele_id(vrs_allele_id=variation.id) # pyright: ignore[reportArgumentType] ) for caf in cafs: From fe697f9b2cef1c07863611bf664e062d74de38ad Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 24 Jun 2026 13:10:12 -0400 Subject: [PATCH 20/21] fix 'http client's 'get_liftover_variation_id' --- src/anyvlm/anyvar/http_client.py | 29 ++++++--------- src/anyvlm/functions/get_cafs.py | 37 ++++---------------- src/anyvlm/restapi/vlm.py | 3 +- src/anyvlm/utils/exceptions.py | 17 +++++++++ src/anyvlm/utils/functions.py | 32 +++++++++++++++++ tests/integration/functions/test_get_cafs.py | 3 +- 6 files changed, 70 insertions(+), 51 deletions(-) create mode 100644 src/anyvlm/utils/exceptions.py create mode 100644 src/anyvlm/utils/functions.py diff --git a/src/anyvlm/anyvar/http_client.py b/src/anyvlm/anyvar/http_client.py index ff98653..e3054d2 100644 --- a/src/anyvlm/anyvar/http_client.py +++ b/src/anyvlm/anyvar/http_client.py @@ -7,7 +7,6 @@ import requests from anyvar.core.metadata import VariationMapping -from anyvar.core.objects import SupportedVrsVariation from anyvar.mapping.liftover import ReferenceAssembly from anyvar.restapi.schema import ( GetMappingResponse, @@ -22,6 +21,8 @@ AnyVarClientError, BaseAnyVarClient, ) +from anyvlm.utils.exceptions import LiftoverError +from anyvlm.utils.functions import validate_allele _logger = logging.getLogger(__name__) @@ -80,20 +81,16 @@ def _make_http_request( raise return response - def retrieve_allele_by_id(self, vrs_id: str) -> SupportedVrsVariation | None: + def retrieve_allele_by_id(self, vrs_id: str) -> models.Allele | None: """Retrieve VRS Allele for given VRS ID :param vrs_id: The ID to dereference :return: The VRS Allele, or `None` if unable to retrieve the Allele. """ url = f"{self.hostname}/object/{vrs_id}" - try: - response = self._make_http_request(method=HTTPMethod.GET, url=url) - except requests.HTTPError: - return None # TODO: add logging - + response = self._make_http_request(method=HTTPMethod.GET, url=url) validated_response: GetObjectResponse = GetObjectResponse(**response.json()) - return validated_response.data + return validate_allele(allele=validated_response.data) def retrieve_allele_by_expression( self, expression: str, assembly: ReferenceAssembly = ReferenceAssembly.GRCH38 @@ -169,19 +166,15 @@ def get_liftover_variation_id( """ as_source: bool = starting_assembly == ReferenceAssembly.GRCH37 url: str = f"{self.hostname}/object/{vrs_id}/mappings/liftover_to?as_source={as_source}" - try: - response = self._make_http_request(HTTPMethod.GET, url) - except requests.HTTPError: - return None - # TODO - handle this (raise exception or return qqch) - + response = self._make_http_request(HTTPMethod.GET, url) validated_response: GetMappingResponse = GetMappingResponse(**response.json()) - if len(validated_response.mappings) > 1: - pass - # raise Exception # TODO - use more specific exception - mapping_result: VariationMapping = validated_response.mappings[0] + variation_mappings: list[VariationMapping] = list(validated_response.mappings) + if len(variation_mappings) > 1: + error_message: str = "Multiple liftover mappings found" + raise LiftoverError(error_message) + mapping_result: VariationMapping = variation_mappings[0] return mapping_result.dest_id if as_source else mapping_result.source_id def close(self) -> None: diff --git a/src/anyvlm/functions/get_cafs.py b/src/anyvlm/functions/get_cafs.py index 6cd9ab5..bf3a55b 100644 --- a/src/anyvlm/functions/get_cafs.py +++ b/src/anyvlm/functions/get_cafs.py @@ -2,12 +2,12 @@ import logging -from anyvar.core.objects import SupportedVrsVariation from ga4gh.core.models import iriReference -from ga4gh.vrs.models import Allele, VrsType +from ga4gh.vrs.models import Allele from anyvlm.anyvar.base_client import BaseAnyVarClient from anyvlm.storage.base_storage import Storage +from anyvlm.utils.functions import validate_allele from anyvlm.utils.types import ( ASSEMBLY_MAP, AnyVlmCohortAlleleFrequencyResult, @@ -20,31 +20,6 @@ _logger = logging.getLogger(__name__) -class VariantLookupError(Exception): - """Raised when a variant cannot be retrieved from AnyVar""" - - -class IncompleteVariantError(Exception): - """Raised when a variant is missing one or more properties required by AnyVLM""" - - -class UnexpectedVariantTypeError(Exception): - """Raised when .type is not of the type expected by AnyVLM""" - - -def _validate_allele(variant: SupportedVrsVariation | None) -> Allele: - if not variant: - raise VariantLookupError - - if not variant.id: - raise IncompleteVariantError - - if not variant.type == str(VrsType.ALLELE.value): - raise UnexpectedVariantTypeError - - return variant - - def _retrieve_cafs_with_resolved_alleles( variation: Allele, anyvlm_storage: Storage ) -> list[AnyVlmCohortAlleleFrequencyResult]: @@ -96,8 +71,8 @@ def get_cafs( msg = "Unsupported assembly ID: {assembly_id}" raise ValueError(msg) from e - vrs_variation: Allele = _validate_allele( - variant=anyvar_client.retrieve_allele_by_expression(gnomad_vcf, assembly) + vrs_variation: Allele = validate_allele( + allele=anyvar_client.retrieve_allele_by_expression(gnomad_vcf, assembly) ) cafs: list[AnyVlmCohortAlleleFrequencyResult] = ( @@ -112,8 +87,8 @@ def get_cafs( ) if liftover_vrs_id: - liftover_variation: Allele = _validate_allele( - variant=anyvar_client.retrieve_allele_by_id(vrs_id=liftover_vrs_id) + liftover_variation: Allele = validate_allele( + allele=anyvar_client.retrieve_allele_by_id(vrs_id=liftover_vrs_id) ) liftover_cafs: list[AnyVlmCohortAlleleFrequencyResult] = ( diff --git a/src/anyvlm/restapi/vlm.py b/src/anyvlm/restapi/vlm.py index 4bd1db5..4c7f18e 100644 --- a/src/anyvlm/restapi/vlm.py +++ b/src/anyvlm/restapi/vlm.py @@ -20,11 +20,12 @@ from anyvlm.anyvar.base_client import AnyVarClientConnectionError, BaseAnyVarClient from anyvlm.functions.build_vlm_response import build_vlm_response -from anyvlm.functions.get_cafs import VariantLookupError, get_cafs +from anyvlm.functions.get_cafs import get_cafs from anyvlm.functions.ingest_vcf import VcfAfColumnsError from anyvlm.functions.ingest_vcf import ingest_vcf as ingest_vcf_function from anyvlm.schemas.vlm import VlmResponse from anyvlm.storage.base_storage import Storage +from anyvlm.utils.exceptions import VariantLookupError from anyvlm.utils.types import ( AnyVlmCohortAlleleFrequencyResult, ChromosomeName, diff --git a/src/anyvlm/utils/exceptions.py b/src/anyvlm/utils/exceptions.py new file mode 100644 index 0000000..5ee08ae --- /dev/null +++ b/src/anyvlm/utils/exceptions.py @@ -0,0 +1,17 @@ +"""Defines custom exceptions for AnyVLM""" + + +class IncompleteVariantError(Exception): + """Raised when a variant is missing one or more properties required by AnyVLM""" + + +class LiftoverError(Exception): + """Raised when an error occurs while attempting to lift over a variant""" + + +class UnexpectedVariantTypeError(Exception): + """Raised when .type is not of the type expected by AnyVLM""" + + +class VariantLookupError(Exception): + """Raised when a variant cannot be retrieved from AnyVar""" diff --git a/src/anyvlm/utils/functions.py b/src/anyvlm/utils/functions.py new file mode 100644 index 0000000..a2614f8 --- /dev/null +++ b/src/anyvlm/utils/functions.py @@ -0,0 +1,32 @@ +"""Defines utility functions for use throughout AnyVLM""" + +from typing import cast + +from anyvar.core.objects import SupportedVrsObject +from ga4gh.vrs.models import Allele + +from anyvlm.utils.exceptions import ( + IncompleteVariantError, + UnexpectedVariantTypeError, + VariantLookupError, +) + + +def validate_allele(allele: SupportedVrsObject | None) -> Allele: + """Validates that the provided object is indeed a VRS Allele, with all the properties AnyVLM requires + + :param allele: The allele we're validating + :return: A VRS Allele object + """ + if not allele: + raise VariantLookupError + + if not allele.id: + raise IncompleteVariantError + + try: + validated_allele: Allele = cast(Allele, allele) + except (ValueError, TypeError) as e: + raise UnexpectedVariantTypeError from e + + return validated_allele diff --git a/tests/integration/functions/test_get_cafs.py b/tests/integration/functions/test_get_cafs.py index e915e6d..6aa8cb8 100644 --- a/tests/integration/functions/test_get_cafs.py +++ b/tests/integration/functions/test_get_cafs.py @@ -5,8 +5,9 @@ from helpers import EXPECTED_VRS_ID, TEST_VARIANT, build_caf from anyvlm.anyvar.python_client import PythonAnyVarClient -from anyvlm.functions.get_cafs import VariantLookupError, get_cafs +from anyvlm.functions.get_cafs import get_cafs from anyvlm.storage.postgres import PostgresObjectStore +from anyvlm.utils.exceptions import VariantLookupError from anyvlm.utils.types import AnyVlmCohortAlleleFrequencyResult From fd4715736657bf981aed91b00039c9ae29bfb2bf Mon Sep 17 00:00:00 2001 From: Jennifer Bowser Date: Wed, 24 Jun 2026 13:51:11 -0400 Subject: [PATCH 21/21] rename cassettes folder from '*get_caf' to '*get_cafs' --- .../test_get_cafs_no_results_returned.yaml | 0 .../test_get_cafs_results_returned.yaml | 0 .../test_get_cafs_variant_not_registered.yaml | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename tests/integration/functions/cassettes/{test_get_caf => test_get_cafs}/test_get_cafs_no_results_returned.yaml (100%) rename tests/integration/functions/cassettes/{test_get_caf => test_get_cafs}/test_get_cafs_results_returned.yaml (100%) rename tests/integration/functions/cassettes/{test_get_caf => test_get_cafs}/test_get_cafs_variant_not_registered.yaml (100%) diff --git a/tests/integration/functions/cassettes/test_get_caf/test_get_cafs_no_results_returned.yaml b/tests/integration/functions/cassettes/test_get_cafs/test_get_cafs_no_results_returned.yaml similarity index 100% rename from tests/integration/functions/cassettes/test_get_caf/test_get_cafs_no_results_returned.yaml rename to tests/integration/functions/cassettes/test_get_cafs/test_get_cafs_no_results_returned.yaml diff --git a/tests/integration/functions/cassettes/test_get_caf/test_get_cafs_results_returned.yaml b/tests/integration/functions/cassettes/test_get_cafs/test_get_cafs_results_returned.yaml similarity index 100% rename from tests/integration/functions/cassettes/test_get_caf/test_get_cafs_results_returned.yaml rename to tests/integration/functions/cassettes/test_get_cafs/test_get_cafs_results_returned.yaml diff --git a/tests/integration/functions/cassettes/test_get_caf/test_get_cafs_variant_not_registered.yaml b/tests/integration/functions/cassettes/test_get_cafs/test_get_cafs_variant_not_registered.yaml similarity index 100% rename from tests/integration/functions/cassettes/test_get_caf/test_get_cafs_variant_not_registered.yaml rename to tests/integration/functions/cassettes/test_get_cafs/test_get_cafs_variant_not_registered.yaml