From d32f1652375790eb77c8f021a6944a095232b3fe Mon Sep 17 00:00:00 2001 From: Aaron Kanzer Date: Mon, 31 Aug 2026 11:24:55 -0400 Subject: [PATCH] fix: stamp bindingdb_release in query() attrs, disclose data license, bound dependency versions query() never set df.attrs["bindingdb_release"], unlike scigantic-chembl's own query(), which stamps df.attrs["chembl_release"] on every call. This is a real API-parity gap between two packages built to mirror each other's shape; fixed to match chembl's exact mechanism. Also: README now discloses BindingDB's data license, which is a per-row mix of CC BY 3.0 (BindingDB's own curated rows) and CC BY-SA 3.0 (rows imported from ChEMBL, per the curation_source column), separate from this package's own MIT-0 code license; chembl_bridge() results specifically carry ChEMBL's share-alike terms on their ChEMBL-matched columns. duckdb/pandas dependencies now carry upper bounds matching scigantic- chembl's; added CHANGELOG.md backfilled from git/tag history; added pytest-cov as a local (non-gating) coverage option. 0.2.5. --- CHANGELOG.md | 37 +++++++++++++++++++++++++++ README.md | 8 +++++- pyproject.toml | 15 ++++++++--- src/scigantic_bindingdb/connection.py | 7 +++-- tests/test_connection.py | 13 ++++++++++ 5 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..09a01c5 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,37 @@ +# Changelog + +All notable changes to this project are documented here. Versions correspond to PyPI releases. + +## 0.2.5 - 2026-08-31 + +- `query()` now stamps `df.attrs["bindingdb_release"]` with the release actually used, matching scigantic-chembl's own `query()`, which already does this for `chembl_release`. +- Added a "Data license" section to the README: BindingDB's underlying data is a per-row mix of CC BY 3.0 (BindingDB's own curated rows) and CC BY-SA 3.0 (rows imported from ChEMBL, keyed by the `curation_source` column), separate from and not superseded by this package's own MIT-0 code license. `chembl_bridge()` results specifically carry ChEMBL's share-alike terms on their ChEMBL-matched columns regardless of the underlying measurement's own source. +- Fixed repo discoverability: added GitHub topics and a homepage link (was previously unset). +- Added upper bounds to the `duckdb` and `pandas` dependency constraints, matching scigantic-chembl's bounds for the same libraries. +- Added `CHANGELOG.md`. +- Added `pytest-cov` as a local, non-CI-gating coverage option. + +## 0.2.4 - 2026-08-29 + +- Coordinate concurrent first downloads of the same cache key: `resolve()`'s check-then-download is now guarded by a per-key lock, so concurrent callers asking for an uncached key wait for one download instead of each starting their own (#3). + +## 0.2.3 - 2026-08-29 + +- Fixed a `chembl_bridge()`/`dti_pairs()` conflict under concurrent calls: both used a named `CREATE OR REPLACE VIEW` on the shared base connection, which raced on DuckDB's catalog under concurrent calls (#2). + +## 0.2.2 - 2026-08-29 + +- `connect()` now reuses the DuckDB base connection across calls instead of rebuilding it every time. +- Fixed a cache download race: the temp filename used during download was deterministic (derived only from the cache key), so two threads racing to fill the same key could collide and raise `FileNotFoundError` on `os.replace()` (#1). + +## 0.2.1 - 2026-08-28 + +- Validate `dti_pairs()`'s `endpoint` argument, matching `measurements()`'s existing validation. + +## 0.2.0 - 2026-08-26 + +- Added `dti_pairs()`, a ready drug-target-interaction training table. + +## 0.1.0 - 2026-08-26 + +- Initial release: query BindingDB from the public S3 mirror with DuckDB. diff --git a/README.md b/README.md index 9c9770b..a5df16c 100644 --- a/README.md +++ b/README.md @@ -132,4 +132,10 @@ $ scigantic-bindingdb query "SELECT count(*) FROM measurements" --release 202608 ## License -MIT-0. See [LICENSE](LICENSE). +MIT-0. See [LICENSE](LICENSE). This covers the code in this package only. + +## Data license + +BindingDB's underlying data is not under one license: it's a row-by-row mix, determined by each measurement's `curation_source` column. BindingDB [licenses](https://www.bindingdb.org/rwd/bind/info.jsp) its own staff-curated rows under [CC BY 3.0](https://creativecommons.org/licenses/by/3.0/), but rows imported from ChEMBL (`curation_source = 'ChEMBL'`, 51.3% of measurements in the 202608 release) carry ChEMBL's own [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/) terms instead, share-alike included. Check `curation_source` before redistributing a subset of `measurements` or `dti_pairs()` to know which terms apply to those specific rows. + +`chembl_bridge()` results specifically warrant care regardless of a matched measurement's own `curation_source`: every row includes `chembl_id`/`chembl_molregno` matched from ChEMBL's own `molecule_dictionary`, and with the default `with_names=True`, ChEMBL's own `pref_name`. Those columns carry ChEMBL's CC BY-SA 3.0 terms, independent of whichever license applies to the rest of that row. diff --git a/pyproject.toml b/pyproject.toml index ef3a9a1..eefc575 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "scigantic-bindingdb" -version = "0.2.4" +version = "0.2.5" description = "Query BindingDB directly from a public S3 mirror with DuckDB, including a ChEMBL cross-reference bridge table and a ready drug-target-interaction training table." readme = "README.md" requires-python = ">=3.10" @@ -25,12 +25,12 @@ classifiers = [ ] dependencies = [ - "duckdb>=0.10", - "pandas>=1.5", + "duckdb>=0.10,<2", + "pandas>=1.5,<4", ] [project.optional-dependencies] -dev = ["pytest>=7", "mypy>=1.10", "pandas-stubs"] +dev = ["pytest>=7", "pytest-cov>=5", "mypy>=1.10", "pandas-stubs"] [project.urls] Homepage = "https://scigantic.com" @@ -49,5 +49,12 @@ scigantic_bindingdb = ["py.typed"] [tool.pytest.ini_options] testpaths = ["tests"] +[tool.coverage.run] +source = ["scigantic_bindingdb"] +# Not wired into CI as a gate: this suite runs real queries against the live +# S3 mirror, so a coverage threshold would be one more thing that can fail +# for reasons unrelated to a change. `pytest --cov` is available locally for +# anyone who wants the number. + [tool.mypy] strict = true diff --git a/src/scigantic_bindingdb/connection.py b/src/scigantic_bindingdb/connection.py index 80eb380..accf90c 100644 --- a/src/scigantic_bindingdb/connection.py +++ b/src/scigantic_bindingdb/connection.py @@ -123,8 +123,11 @@ def query(sql: str, release: str | None = None) -> "pd.DataFrame": For several queries against the same release, call connect() once and reuse it instead, to avoid opening and closing a cursor per query. """ - con = connect(release) + resolved_release = release or latest() + con = connect(resolved_release) try: - return con.execute(sql).df() + df = con.execute(sql).df() + df.attrs["bindingdb_release"] = resolved_release + return df finally: con.close() diff --git a/tests/test_connection.py b/tests/test_connection.py index 33a154e..970d084 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -20,6 +20,19 @@ def test_query_returns_dataframe_with_expected_columns(): assert len(df) == 5 +def test_query_records_bindingdb_release_in_attrs(): + # Matches scigantic_chembl's query(), which stamps df.attrs["chembl_release"] + # the same way: the release actually used should travel with the result, + # not just be implicit in whatever the caller happened to pass. + df = bindingdb.query("SELECT 1", release="202608") + assert df.attrs["bindingdb_release"] == "202608" + + +def test_query_omitted_release_records_resolved_release_in_attrs(): + df = bindingdb.query("SELECT 1") + assert df.attrs["bindingdb_release"] == bindingdb.latest() + + def test_connect_registers_all_five_core_tables(): con = bindingdb.connect() try: