diff --git a/04_workdir.yaml b/04_workdir.yaml index fb24198..5bcf729 100644 --- a/04_workdir.yaml +++ b/04_workdir.yaml @@ -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] @@ -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 diff --git a/README.md b/README.md index 47371ed..3c68b68 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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) ``` @@ -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" diff --git a/docs/learn/tutorials/quickstart.md b/docs/learn/tutorials/quickstart.md index 75be3b5..51e8fe9 100644 --- a/docs/learn/tutorials/quickstart.md +++ b/docs/learn/tutorials/quickstart.md @@ -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) ``` @@ -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) ``` diff --git a/docs/learn/tutorials/tutorial0001_basics.md b/docs/learn/tutorials/tutorial0001_basics.md index f824908..2a417eb 100644 --- a/docs/learn/tutorials/tutorial0001_basics.md +++ b/docs/learn/tutorials/tutorial0001_basics.md @@ -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. --- diff --git a/docs/reference/source/ontograph/client-catalog.md b/docs/reference/source/ontograph/client-catalog.md index bb0cee9..4bdf066 100644 --- a/docs/reference/source/ontograph/client-catalog.md +++ b/docs/reference/source/ontograph/client-catalog.md @@ -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) ``` diff --git a/docs/reference/source/ontograph/client-ontology.md b/docs/reference/source/ontograph/client-ontology.md index 723680a..a700222 100644 --- a/docs/reference/source/ontograph/client-ontology.md +++ b/docs/reference/source/ontograph/client-ontology.md @@ -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) ``` diff --git a/docs/reference/source/ontograph/downloader.md b/docs/reference/source/ontograph/downloader.md index 41b6fca..356886c 100644 --- a/docs/reference/source/ontograph/downloader.md +++ b/docs/reference/source/ontograph/downloader.md @@ -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 diff --git a/ontograph/client.py b/ontograph/client.py index 9f18e48..e0afee6 100644 --- a/ontograph/client.py +++ b/ontograph/client.py @@ -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, @@ -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 --- # # --------------------------------------------- # @@ -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. @@ -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 @@ -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: @@ -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'. @@ -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) @@ -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 = [ @@ -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." diff --git a/ontograph/loader.py b/ontograph/loader.py index a0b980c..22fa7ff 100644 --- a/ontograph/loader.py +++ b/ontograph/loader.py @@ -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)', @@ -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', diff --git a/sandbox/use_case_downloaders.py b/sandbox/use_case_downloaders.py index ed3adfc..b7636ef 100644 --- a/sandbox/use_case_downloaders.py +++ b/sandbox/use_case_downloaders.py @@ -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() diff --git a/tests/test_client.py b/tests/test_client.py index 6e04dd9..6f31cc9 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -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 @@ -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 @@ -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'