From f8320af588ece95603712ddc024448e6622f5276 Mon Sep 17 00:00:00 2001 From: Kyle Ferriter Date: Tue, 27 Oct 2026 14:54:23 -0400 Subject: [PATCH 1/2] build!: replace dill with stdlib pickle in object store Sqlite3MutableMapping used dill to serialize values. Nothing in the library needs dill's by-value pickling: enref/deref only stores VRS models, which pickle by reference. The only dependent was test_classes, which stored a class defined inside the test function. Move that class to module level, switch to pickle, and drop the dill dependency. dill 0.3.x also fails on Python 3.15, where code objects no longer have co_lnotab. Values written by dill for importable classes contain no dill references and load with pickle. Co-Authored-By: Claude Opus 5.5 --- pyproject.toml | 1 - src/ga4gh/vrs/extras/object_store.py | 7 +++--- tests/extras/test_object_store.py | 32 +++++++++++++++++----------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f9e76d3f..b89769f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,6 @@ extras = [ "biocommons.seqrepo>=0.5.1", "bioutils>=0.5.2", "hgvs>=2.0.0a0,<3.0", - "dill~=0.3.7", "click", "pysam>=0.24.0", ] diff --git a/src/ga4gh/vrs/extras/object_store.py b/src/ga4gh/vrs/extras/object_store.py index 8712bb31..576da868 100644 --- a/src/ga4gh/vrs/extras/object_store.py +++ b/src/ga4gh/vrs/extras/object_store.py @@ -1,10 +1,9 @@ +import pickle import sqlite3 from collections.abc import MutableMapping from threading import Lock from typing import Any -import dill - class Sqlite3MutableMapping(MutableMapping): """Class that can be used like a Python dictionary but that uses a sqlite3 database @@ -59,7 +58,7 @@ def __delitem__(self, key: Any) -> None: def __setitem__(self, key: Any, value: Any) -> None: cur = self.db.cursor() try: - ser = dill.dumps(value) + ser = pickle.dumps(value) cur.execute( "insert or replace into mapping(key, value) values (?, ?)", (key, sqlite3.Binary(ser)), @@ -75,7 +74,7 @@ def __getitem__(self, key: Any) -> Any: rows = cur.execute("select value from mapping where key = ?", (key,)) row0 = next(rows) if row0: - return dill.loads(row0[0]) # noqa: S301 + return pickle.loads(row0[0]) # noqa: S301 except StopIteration as e: raise KeyError("Key not found: " + str(key)) from e finally: diff --git a/tests/extras/test_object_store.py b/tests/extras/test_object_store.py index 4a11e27a..2d65910b 100644 --- a/tests/extras/test_object_store.py +++ b/tests/extras/test_object_store.py @@ -55,23 +55,31 @@ def test_complex(tmp_path): object_store.close() -def test_classes(tmp_path): - db_path = str(tmp_path) + "/test_complex.sqlite3" +class StoredObject: + """Custom class for object store round-trip tests. + + Defined at module level because pickle stores classes by reference, and a class + local to the test function can't be imported when unpickling. + """ + + def __init__(self, id_value): + """Initialize with an id and some nested attribute values.""" + self.id = id_value + self.A = "A" + self.B = ["B1", "B2", 3] + self.C = {"C1": "C1-value"} - class TestClass: - def __init__(self, id_value): - self.id = id_value - self.A = "A" - self.B = ["B1", "B2", 3] - self.C = {"C1": "C1-value"} + def somefunction(self, arg): + return f"{self.id}-{arg}" - def somefunction(self, arg): - return f"{self.id}-{arg}" + +def test_classes(tmp_path): + db_path = str(tmp_path) + "/test_complex.sqlite3" object_store = Sqlite3MutableMapping(db_path) - val1 = TestClass("val1") - val2 = TestClass("val2") + val1 = StoredObject("val1") + val2 = StoredObject("val2") object_store["val1"] = val1 object_store["val2"] = val2 From 83e35757fa09d2e46558058be1156a287efacfbd Mon Sep 17 00:00:00 2001 From: Kyle Ferriter Date: Tue, 27 Oct 2026 14:54:39 -0400 Subject: [PATCH 2/2] build: allow python 3.15 The <3.15 cap was only needed for dill compatibility, and dill is no longer a dependency. Add 3.15 to the classifiers and CI matrix. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/python-cqa.yml | 2 +- pyproject.toml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-cqa.yml b/.github/workflows/python-cqa.yml index 7a13245d..741bfd78 100644 --- a/.github/workflows/python-cqa.yml +++ b/.github/workflows/python-cqa.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.11", "3.12", "3.13", "3.14"] + python-version: ["3.11", "3.12", "3.13", "3.14", "3.15"] env: SEQREPO_ROOT_DIR: ./tests/data/seqrepo/latest steps: diff --git a/pyproject.toml b/pyproject.toml index b89769f1..afd6613c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Topic :: Scientific/Engineering :: Bio-Informatics", "Topic :: Scientific/Engineering :: Medical Science Apps.", ] @@ -33,7 +34,7 @@ keywords = [ "spdi", "variation" ] -requires-python = ">=3.11,<3.15" +requires-python = ">=3.11" dynamic = ["version"] dependencies = [ "pydantic~=2.1",