Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ exclude: '^docs/conf.py'

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: check-added-large-files
Expand All @@ -19,7 +19,7 @@ repos:

- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.8.2
rev: v0.16.0
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand Down
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changelog

## Version 0.0.1
## Version 0.0.1

- Initial implementation to access OrgDB objects.
- Initial implementation to access OrgDB objects.
- This also fetches the annotation hub sqlite file and queries for available org sqlite files instead of a static registry used in the txdb package.
4 changes: 2 additions & 2 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
furo
myst-nb
# Requirements file for ReadTheDocs, check .readthedocs.yml.
# To build the module reference correctly, make sure every external package
# under `install_requires` in `setup.cfg` is also listed here!
# sphinx_rtd_theme
myst-parser[linkify]
sphinx>=3.2.1
myst-nb
furo
sphinx-autodoc-typehints
12 changes: 6 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
"""
Setup file for orgdb.
Use setup.cfg to configure your project.
Setup file for orgdb.
Use setup.cfg to configure your project.

This file was generated with PyScaffold 4.6.
PyScaffold helps you to put up the scaffold of your new Python project.
Learn more under: https://pyscaffold.org/
This file was generated with PyScaffold 4.6.
PyScaffold helps you to put up the scaffold of your new Python project.
Learn more under: https://pyscaffold.org/
"""

from setuptools import setup

if __name__ == "__main__":
try:
setup(use_scm_version={"version_scheme": "no-guess-dev"})
except: # noqa
except:
print(
"\n\nAn error occurred while building the project, "
"please ensure you have the most updated version of setuptools, "
Expand Down
2 changes: 1 addition & 1 deletion src/orgdb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@
from .orgdbregistry import OrgDbRegistry
from .record import OrgDbRecord

__all__ = ["OrgDb", "OrgDbRegistry", "OrgDbRecord"]
__all__ = ["OrgDb", "OrgDbRecord", "OrgDbRegistry"]
22 changes: 9 additions & 13 deletions src/orgdb/orgdb.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import sqlite3
from typing import Dict, List, Union

from biocframe import BiocFrame
from genomicranges import GenomicRanges
Expand Down Expand Up @@ -68,7 +67,7 @@ def species(self) -> str:
return v
return "Unknown"

def _define_tables(self) -> Dict[str, tuple]:
def _define_tables(self) -> dict[str, tuple]:
"""Define the mapping between column names and (table, field).

Mirrors .definePossibleTables from R/methods-geneCentricDbs.R
Expand Down Expand Up @@ -111,8 +110,7 @@ def _define_tables(self) -> Dict[str, tuple]:
}

if db_class == "OrgDb":
if "ALIAS2PROBE" in mapping:
del mapping["ALIAS2PROBE"]
mapping.pop("ALIAS2PROBE", None)

if db_class == "ChipDb":
mapping["PROBEID"] = ("c.probes", "probe_id")
Expand Down Expand Up @@ -379,15 +377,15 @@ def _define_tables(self) -> Dict[str, tuple]:

return mapping

def columns(self) -> List[str]:
def columns(self) -> list[str]:
"""List all available columns/keytypes."""
return list(self._table_map.keys())

def keytypes(self) -> List[str]:
def keytypes(self) -> list[str]:
"""List all available keytypes (same as columns)."""
return self.columns()

def keys(self, keytype: str) -> List[str]:
def keys(self, keytype: str) -> list[str]:
"""Return keys for the given keytype."""
if keytype not in self._table_map:
raise ValueError(f"Invalid keytype: {keytype}. Use columns() to see valid options.")
Expand All @@ -404,7 +402,7 @@ def keys(self, keytype: str) -> List[str]:
except sqlite3.OperationalError:
return []

def _expand_cols(self, cols: List[str]) -> List[str]:
def _expand_cols(self, cols: list[str]) -> list[str]:
"""Expand columns like GO into GO, EVIDENCE, ONTOLOGY."""
new_cols = []
for c in cols:
Expand All @@ -419,7 +417,7 @@ def _expand_cols(self, cols: List[str]) -> List[str]:
new_cols.append("CHRLOCCHR")
return list(set(new_cols)) # remove duplicates

def select(self, keys: Union[List[str], str], columns: Union[List[str], str], keytype: str) -> BiocFrame:
def select(self, keys: list[str] | str, columns: list[str] | str, keytype: str) -> BiocFrame:
"""Retrieve data from the database.

Args:
Expand Down Expand Up @@ -482,9 +480,7 @@ def select(self, keys: Union[List[str], str], columns: Union[List[str], str], ke

return self._query_as_biocframe(sql, tuple(keys))

def mapIds(
self, keys: Union[List[str], str], column: str, keytype: str, multiVals: str = "first"
) -> Union[dict, list]:
def mapIds(self, keys: list[str] | str, column: str, keytype: str, multiVals: str = "first") -> dict | list:
"""Map keys to a specific column. A wrapper around select.

Args:
Expand Down Expand Up @@ -543,7 +539,7 @@ def genes(self) -> GenomicRanges:
return GenomicRanges.empty()

query = """
SELECT
SELECT
g.gene_id,
c.seqname,
c.start_location,
Expand Down
10 changes: 5 additions & 5 deletions src/orgdb/orgdbregistry.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os
import sqlite3
from pathlib import Path
from typing import Any, Dict, List, Optional, Union
from typing import Any

from pybiocfilecache import BiocFileCache

Expand All @@ -19,7 +19,7 @@ class OrgDbRegistry:

def __init__(
self,
cache_dir: Optional[Union[str, Path]] = None,
cache_dir: str | Path | None = None,
force: bool = False,
) -> None:
"""Initialize the OrgDb registry.
Expand All @@ -39,7 +39,7 @@ def __init__(
self._cache_dir.mkdir(parents=True, exist_ok=True)
self._bfc = BiocFileCache(self._cache_dir)

self._registry_map: Dict[str, OrgDbRecord] = {}
self._registry_map: dict[str, OrgDbRecord] = {}

self._initialize_registry(force=force)

Expand Down Expand Up @@ -108,7 +108,7 @@ def _initialize_registry(self, force: bool = False):
record = OrgDbRecord.from_config_entry(orgdb_id, entry)
self._registry_map[orgdb_id] = record

def list_orgdb(self) -> List[str]:
def list_orgdb(self) -> list[str]:
"""List all available OrgDb IDs (e.g., 'org.Hs.eg.db').

Returns:
Expand Down Expand Up @@ -205,7 +205,7 @@ def load_db(self, orgdb_id: str, force: bool = False) -> OrgDb:
path = self.download(orgdb_id, force=force)
return OrgDb(path)

def _get_filepath(self, resource: Any) -> Optional[str]:
def _get_filepath(self, resource: Any) -> str | None:
"""Helper to extract absolute path from a BiocFileCache resource."""
if hasattr(resource, "rpath"):
rel_path = str(resource.rpath)
Expand Down
24 changes: 10 additions & 14 deletions src/orgdb/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from dataclasses import dataclass
from datetime import date, datetime
from typing import Optional

__author__ = "Jayaram Kancherla"
__copyright__ = "Jayaram Kancherla"
Expand All @@ -14,16 +13,16 @@ class OrgDbRecord:
"""Container for a single OrgDb entry."""

orgdb_id: str
release_date: Optional[date]
release_date: date | None
url: str

species: Optional[str] = None # e.g. "Hs" or "Hsapiens"
id_type: Optional[str] = None # e.g. "eg" (Entrez Gene) or "tair"
species: str | None = None # e.g. "Hs" or "Hsapiens"
id_type: str | None = None # e.g. "eg" (Entrez Gene) or "tair"

bioc_version: Optional[str] = None
bioc_version: str | None = None

@classmethod
def from_config_entry(cls, orgdb_id: str, entry: dict) -> "OrgDbRecord":
def from_config_entry(cls, orgdb_id: str, entry: dict) -> OrgDbRecord:
"""Build a record from a ORGDB_CONFIG entry:
{
"release_date": "YYYY-MM-DD", # optional
Expand All @@ -33,7 +32,7 @@ def from_config_entry(cls, orgdb_id: str, entry: dict) -> "OrgDbRecord":
url = entry["url"]

date_str = entry.get("release_date")
rel_date: Optional[date]
rel_date: date | None
if date_str:
rel_date = datetime.strptime(date_str, "%Y-%m-%d").date()
else:
Expand All @@ -59,14 +58,11 @@ def _parse_orgdb_id(orgdb_id: str):
into (species, id_type).
"""
name = orgdb_id
if name.startswith("org."):
name = name[len("org.") :]
name = name.removeprefix("org.")

if name.endswith(".db"):
name = name[: -len(".db")]
name = name.removesuffix(".db")

if name.endswith(".sqlite"):
name = name[: -len(".sqlite")]
name = name.removesuffix(".sqlite")

parts = name.split(".")

Expand All @@ -79,7 +75,7 @@ def _parse_orgdb_id(orgdb_id: str):
return species, id_type


def _parse_bioc_version(url: str) -> Optional[str]:
def _parse_bioc_version(url: str) -> str | None:
"""Extract the Bioconductor/AnnotationHub-like version from URL.

Example:
Expand Down
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
- https://docs.pytest.org/en/stable/writing_plugins.html
"""

import sqlite3
import sqlite3
from orgdb import OrgDb
import pytest

Expand Down Expand Up @@ -102,4 +102,4 @@ def mock_orgdb(mock_orgdb_path):
"""Return an open OrgDb instance using the mock database."""
db = OrgDb(mock_orgdb_path)
yield db
db.close()
db.close()
16 changes: 8 additions & 8 deletions tests/test_orgdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,19 @@ def test_select_simple(mock_orgdb):
def test_select_multikey(mock_orgdb):
res = mock_orgdb.select(keys=["1", "10"], columns=["SYMBOL"], keytype="ENTREZID")
assert len(res) == 2

symbols = res.get_column("SYMBOL")
assert "A1BG" in symbols
assert "NAT2" in symbols

def test_select_go_expansion(mock_orgdb):
res = mock_orgdb.select(keys="1", columns=["GO"], keytype="ENTREZID")

col_names = list(res.column_names)
assert "GO" in col_names
assert "EVIDENCE" in col_names
assert "ONTOLOGY" in col_names

assert len(res) == 2
go_ids = res.get_column("GO")
assert "GO:0000001" in go_ids
Expand All @@ -66,12 +66,12 @@ def test_select_many_to_one(mock_orgdb):

def test_mapIds(mock_orgdb):
keys = ["1", "10", "100"]

res = mock_orgdb.mapIds(keys, column="SYMBOL", keytype="ENTREZID")
assert isinstance(res, dict)
assert res["1"] == "A1BG"
assert res["10"] == "NAT2"

res_list = mock_orgdb.mapIds(["1"], column="GO", keytype="ENTREZID", multiVals="list")
assert isinstance(res_list["1"], list)
assert len(res_list["1"]) == 2
Expand All @@ -81,12 +81,12 @@ def test_genes_genomicranges(mock_orgdb):
gr = mock_orgdb.genes()
assert isinstance(gr, GenomicRanges)
assert len(gr) == 2

names = list(gr.names)
idx = names.index("1")
assert str(gr.seqnames[idx]) == "chr19"
assert gr.start[idx] == 58346806
assert gr.end[idx] == 58353492

assert "gene_id" in gr.mcols.column_names
assert gr.mcols.get_column("gene_id")[idx] == "1"
assert gr.mcols.get_column("gene_id")[idx] == "1"
2 changes: 1 addition & 1 deletion tests/test_real.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ def test_real_orgdb_workflow(tmp_path):
"GO:0048699",
"GO:0048143"],
columns="SYMBOL")

assert res.shape == (104, 4)
orgdb.close()
2 changes: 1 addition & 1 deletion tests/test_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def registry(tmp_path):
def test_registry_init(registry):
assert isinstance(registry, OrgDbRegistry)
assert "org.Hs.eg.db" in registry.list_orgdb()

def test_get_record(registry):
rec = registry.get_record("org.Hs.eg.db")
assert rec.orgdb_id == "org.Hs.eg.db"
Expand Down