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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions 04_workdir.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ logging:
filename: logs/app_go_logger.log
loggers:
default:
level: DEBUG
level: WARNING

# Logger configuration for each package
pkg_infra:
level: INFO
handlers: [file, console]
propagate: false

download_manager:
level: INFO
handlers: [file, console]
Expand All @@ -30,6 +31,7 @@ integrations: # New question: store the big yaml?
settings:
- enabled: true # This will be switch <--- what should be the default value?
- cache_path: ontograph_config.yaml
- downloader: download_manager
- source_ontology: chebi
- backend: graphblas

Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ from ontograph.config.settings import DEFAULT_CACHE_DIR
# Instantiate a client for your catalog
client_catalog = ClientCatalog(cache_dir="./data/out")

# Optional: choose a downloader adapter explicitly
# Optional: choose downloader via string or adapter (client-level only)
# client_catalog = ClientCatalog(cache_dir="./data/out", downloader="download_manager")
# downloader = DownloadManagerAdapter(cache_dir=DEFAULT_CACHE_DIR, backend="requests")
# client_catalog = ClientCatalog(cache_dir="./data/out", downloader=downloader)

Expand Down Expand Up @@ -71,7 +72,8 @@ client_dummy_ontology = ClientOntology(cache_dir="./data/out")
# Load a dummy ontology, we prepare a simple one to try out this package.
client_dummy_ontology.load(source="./tests/resources/dummy_ontology.obo")

# Optional: choose a downloader adapter explicitly
# Optional: choose downloader via string or adapter (client-level only)
# client_dummy_ontology = ClientOntology(cache_dir="./data/out", downloader="pooch")
# downloader = DownloadManagerAdapter(cache_dir=DEFAULT_CACHE_DIR, backend="requests")
# client_dummy_ontology = ClientOntology(cache_dir="./data/out", downloader=downloader)
```
Expand Down Expand Up @@ -158,6 +160,7 @@ client_go.load(source="go")
By default, the project uses a configurable downloader backend. You can set a global default in `ontograph/config/settings.py`:

```python
# Used as the fallback when no downloader is provided
DEFAULT_DOWNLOADER = "pooch"
# or
DEFAULT_DOWNLOADER = "download_manager"
Expand Down
6 changes: 4 additions & 2 deletions docs/learn/tutorials/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ client_catalog = ClientCatalog(cache_dir="./data/out")
# Load the catalog (downloads if not cached)
client_catalog.load_catalog()

# Optional: choose a downloader adapter explicitly
# Optional: choose downloader via string or adapter (client-level only)
# client_catalog = ClientCatalog(cache_dir="./data/out", downloader="download_manager")
# downloader = DownloadManagerAdapter(cache_dir=DEFAULT_CACHE_DIR, backend="requests")
# client_catalog = ClientCatalog(cache_dir="./data/out", downloader=downloader)
```
Expand Down Expand Up @@ -63,7 +64,8 @@ client_ontology = ClientOntology(cache_dir="./data/out")
# Load a sample ontology (provided in the repo for testing)
client_ontology.load(source="./tests/resources/dummy_ontology.obo")

# Optional: choose a downloader adapter explicitly
# Optional: choose downloader via string or adapter (client-level only)
# client_ontology = ClientOntology(cache_dir="./data/out", downloader="pooch")
# downloader = DownloadManagerAdapter(cache_dir=DEFAULT_CACHE_DIR, backend="requests")
# client_ontology = ClientOntology(cache_dir="./data/out", downloader=downloader)
```
Expand Down
1 change: 1 addition & 0 deletions docs/learn/tutorials/tutorial0001_basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ DEFAULT_DOWNLOADER = "download_manager"
```

Now any client will use the configured backend unless you pass a downloader explicitly.
Only client classes accept backend strings; lower-level components expect a downloader adapter instance.

---

Expand Down
4 changes: 3 additions & 1 deletion docs/reference/source/ontograph/client-catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ from ontograph.config.settings import DEFAULT_CACHE_DIR

client_catalog = ClientCatalog(cache_dir="./data/out")

# Optional: use Download Manager for all catalog downloads
# Optional: use a backend string (client-level only)
# client_catalog = ClientCatalog(cache_dir="./data/out", downloader="download_manager")
# Or pass the adapter explicitly
# downloader = DownloadManagerAdapter(cache_dir=DEFAULT_CACHE_DIR, backend="requests")
# client_catalog = ClientCatalog(cache_dir="./data/out", downloader=downloader)
```
Expand Down
4 changes: 3 additions & 1 deletion docs/reference/source/ontograph/client-ontology.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ from ontograph.config.settings import DEFAULT_CACHE_DIR

client_ontology = ClientOntology(cache_dir="./data/out")

# Optional: use Download Manager for all remote downloads
# Optional: use a backend string (client-level only)
# client_ontology = ClientOntology(cache_dir="./data/out", downloader="pooch")
# Or pass the adapter explicitly
# downloader = DownloadManagerAdapter(cache_dir=DEFAULT_CACHE_DIR, backend="requests")
# client_ontology = ClientOntology(cache_dir="./data/out", downloader=downloader)
```
Expand Down
4 changes: 4 additions & 0 deletions docs/reference/source/ontograph/downloader.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ from ontograph.config.settings import DEFAULT_CACHE_DIR
downloader = get_default_downloader(cache_dir=DEFAULT_CACHE_DIR)
```

Note: String backends (e.g., `"pooch"`, `"download_manager"`) are accepted
at the client layer (`ClientCatalog` / `ClientOntology`). Lower-level
components expect a downloader adapter instance.

---

## API Reference
Expand Down
56 changes: 40 additions & 16 deletions ontograph/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
NodesDataframe,
CatalogOntologies,
)
from ontograph.downloader import DownloaderPort
from ontograph.downloader import DownloaderPort, get_default_downloader
from ontograph.config.settings import DEFAULT_CACHE_DIR
from ontograph.queries.navigator import (
NavigatorPronto,
Expand All @@ -58,6 +58,16 @@
]


def _resolve_downloader(
cache_dir: Path, downloader: DownloaderPort | str | None
) -> DownloaderPort:
if isinstance(downloader, str):
return get_default_downloader(cache_dir=cache_dir, backend=downloader)
if downloader is None:
return get_default_downloader(cache_dir=cache_dir, backend='pooch')
return downloader


# --------------------------------------------- #
# ---- Client for Catalog --- #
# --------------------------------------------- #
Expand All @@ -76,19 +86,22 @@ class ClientCatalog:
def __init__(
self,
cache_dir: str = DEFAULT_CACHE_DIR,
downloader: DownloaderPort | None = None,
downloader: DownloaderPort | str | None = None,
) -> None:
"""Initialize the ClientCatalog.

Args:
cache_dir (str, optional): Directory for caching catalog data. Defaults to DEFAULT_CACHE_DIR.
downloader (DownloaderPort | None, optional): Downloader adapter for remote resources. Defaults to None.
downloader (DownloaderPort | str | None, optional): Downloader adapter or backend name
('pooch' or 'download_manager'). Defaults to 'pooch'.
"""
cache_path = Path(cache_dir)
resolved_downloader = _resolve_downloader(cache_path, downloader)
self.__catalog_adapter = CatalogOntologies(
cache_dir=Path(cache_dir),
downloader=downloader,
cache_dir=cache_path,
downloader=resolved_downloader,
)
self._downloader = downloader
self._downloader = resolved_downloader

def load_catalog(self, force_download: bool = False) -> None:
"""Load the ontology catalog.
Expand Down Expand Up @@ -231,17 +244,18 @@ class ClientOntology:
def __init__(
self,
cache_dir: str = DEFAULT_CACHE_DIR,
downloader: DownloaderPort | None = None,
downloader: DownloaderPort | str | None = None,
) -> None:
"""Initialize the ClientOntology.

Args:
cache_dir (str, optional): Directory for caching ontology data. Defaults to DEFAULT_CACHE_DIR.
downloader (DownloaderPort | None, optional): Downloader adapter for remote resources. Defaults to None.
downloader (DownloaderPort | str | None, optional): Downloader adapter or backend name
('pooch' or 'download_manager'). Defaults to 'pooch'.
"""
self._cache_dir = Path(cache_dir)
self._downloader = _resolve_downloader(self._cache_dir, downloader)
self._ontology = None
self._downloader = downloader
self._lookup_tables = None
self._navigator = None
self._relations = None
Expand Down Expand Up @@ -331,7 +345,7 @@ def _detect_source_type(self, source: str) -> str:
def load(
self,
source: str,
downloader: DownloaderPort = None,
downloader: DownloaderPort | str | None = None,
include_obsolete: bool = False,
backend: str = 'pronto',
) -> None:
Expand All @@ -346,7 +360,8 @@ def load(

Args:
source (str): Path to the ontology file, URL, or OBO Foundry identifier.
downloader (DownloaderPort, optional): Downloader adapter for remote files. Defaults to None.
downloader (DownloaderPort | str | None, optional): Downloader adapter or backend name
('pooch' or 'download_manager'). Defaults to None.
include_obsolete (bool, optional): If True, include obsolete terms when building GraphBLAS structures. Defaults to False.
backend (str, optional): Backend for queries ('pronto' or 'graphblas'). Defaults to 'pronto'.

Expand All @@ -362,12 +377,19 @@ def load(
>>> client.load(source="./tests/resources/dummy_ontology.obo")
"""
logger.info(f'Loading ontology from source: {source} ...')
resolved_downloader = (
_resolve_downloader(self._cache_dir, downloader)
if downloader is not None
else self._downloader
)
logger.debug(
'Using downloader: %s',
type(downloader).__name__ if downloader else 'default',
type(resolved_downloader).__name__
if resolved_downloader
else 'default',
)
loader = ProntoLoaderAdapter(
cache_dir=self._cache_dir, downloader=self._downloader
cache_dir=self._cache_dir, downloader=resolved_downloader
)

path = Path(source)
Expand All @@ -388,14 +410,16 @@ def load(
f'Detected URL source, downloading ontology from {source}'
)
filename = Path(source).name or 'ontology.obo'
ontology = loader.load_from_url(source, filename, downloader)
ontology = loader.load_from_url(
source, filename, resolved_downloader
)

# 3. Case 3: Try OBO catalog (if file missing or simple ID)
else:
logger.debug('Resolved source type: catalog')
catalog_client = ClientCatalog(
cache_dir=self._cache_dir,
downloader=self._downloader,
downloader=resolved_downloader,
)
catalog_client.load_catalog()
available = [
Expand All @@ -410,7 +434,7 @@ def load(
ontology = loader.load_from_catalog(
name_id=name_id,
format='obo',
downloader=self._downloader,
downloader=resolved_downloader,
)
else:
msg = f"Ontology '{source}' not found as file, URL, or catalog entry."
Expand Down
8 changes: 2 additions & 6 deletions ontograph/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,9 +410,7 @@ def _download_ontology(
downloader = self._downloader
if downloader is None:
downloader = get_default_downloader(cache_dir=self.cache_dir)
logger.debug(
f'Created default downloader: {type(downloader).__name__}'
)
logger.debug(f'Created default downloader: {type(downloader).__name__}')

logger.info(
'Downloading ontology %s.%s using %s (catalog)',
Expand Down Expand Up @@ -520,9 +518,7 @@ def load_from_url(
downloader = self._downloader
if downloader is None:
downloader = get_default_downloader(cache_dir=self.cache_dir)
logger.debug(
f'Created default downloader: {type(downloader).__name__}'
)
logger.debug(f'Created default downloader: {type(downloader).__name__}')

logger.info(
'Downloading ontology from URL using %s: %s',
Expand Down
4 changes: 4 additions & 0 deletions sandbox/use_case_downloaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ def main():
client = ClientOntology(cache_dir=cache_dir, downloader=downloader)
client.load(source='go') # catalog download

# Print roots of GO ontology
logger.info("Roots GO ontology: ")
logger.info(f"{client.get_root()}")

if __name__ == '__main__':

main()
43 changes: 42 additions & 1 deletion tests/test_client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pathlib import Path

import pytest
import ontograph.client as client_module

from ontograph.client import ClientCatalog, ClientOntology
from ontograph.models import Ontology, TermList
Expand Down Expand Up @@ -117,7 +118,7 @@ def test_get_download_url_and_formats(client_catalog):

def test_client_ontology_load_from_file(client_ontology, dummy_ontology_path):
# Should load ontology from file and initialize queries
ontology = client_ontology.load(source=str(dummy_ontology_path))
client_ontology.load(source=str(dummy_ontology_path))
# The new load method does not return ontology, so check internal state
assert isinstance(client_ontology._ontology, Ontology)
# Should be able to access root term
Expand Down Expand Up @@ -187,3 +188,43 @@ def test_client_ontology_introspection_methods(
assert isinstance(path, list)
trajectories = client_ontology.get_trajectories_from_root('D')
assert isinstance(trajectories, list)


def test_client_catalog_downloader_string_uses_backend(tmp_path, monkeypatch):
class DummyDownloader:
pass

calls = {}

def fake_get_default(cache_dir, *, backend=None):
calls['cache_dir'] = cache_dir
calls['backend'] = backend
return DummyDownloader()

monkeypatch.setattr(
client_module, 'get_default_downloader', fake_get_default
)

client = ClientCatalog(cache_dir=tmp_path, downloader='download_manager')
assert isinstance(client._downloader, DummyDownloader)
assert calls['backend'] == 'download_manager'


def test_client_ontology_downloader_string_uses_backend(tmp_path, monkeypatch):
class DummyDownloader:
pass

calls = {}

def fake_get_default(cache_dir, *, backend=None):
calls['cache_dir'] = cache_dir
calls['backend'] = backend
return DummyDownloader()

monkeypatch.setattr(
client_module, 'get_default_downloader', fake_get_default
)

client = ClientOntology(cache_dir=tmp_path, downloader='pooch')
assert isinstance(client._downloader, DummyDownloader)
assert calls['backend'] == 'pooch'
Loading