Source code for pycsamt.backends

# Author: LKouadio <etanoyau@gmail.com>
# License: LGPL-3.0
"""Backend abstraction and lazy discovery for deep-learning frameworks.

pycsamt's AI/ML modules support both PyTorch and TensorFlow.  Neither
framework is a required dependency of the package — they are loaded
lazily only when the AI module is actually used.

Configuration
-------------
The active backend is resolved using this priority chain:

1. Explicit call to :func:`set_backend` in the current Python session.
2. ``PYCSAMT_AI_BACKEND`` environment variable
   (``torch`` / ``tensorflow`` / ``auto``).
3. ``~/.pycsamt/config.json`` key ``"ai_backend"``.
4. Auto-detection: first available framework in ``['torch', 'tensorflow']``.

Quick start
-----------
>>> import pycsamt
>>> pycsamt.list_backends()
{'torch': True, 'tensorflow': False}
>>> pycsamt.set_backend('torch')      # or 'tensorflow' / 'auto'
>>> pycsamt.get_backend()
'torch'

Environment variable
--------------------
Set before importing pycsamt to override the default::

    PYCSAMT_AI_BACKEND=tensorflow python my_script.py
"""

from __future__ import annotations

from typing import Any, Dict, Optional

from ._config import BackendConfig
from ._detect import (
    detect_available_backends,
    get_backend_versions,
    probe_backend,
)

__all__ = [
    "get_backend",
    "set_backend",
    "auto_detect",
    "list_backends",
    "get_backend_instance",
    "detect_available_backends",
    "probe_backend",
    "get_backend_versions",
]

_CFG = BackendConfig()


# ─────────────────────────────────────────────────────────────────────────────
# Public API
# ─────────────────────────────────────────────────────────────────────────────


[docs] def get_backend() -> str: """ Return the name of the currently active backend. Returns ------- name : str ``'torch'``, ``'tensorflow'``, or ``'none'`` when no DL framework is installed. Examples -------- >>> from pycsamt.backends import get_backend >>> get_backend() 'torch' """ return _CFG.backend_name
[docs] def set_backend(name: str, *, persist: bool = False) -> None: """ Set the active AI backend. Parameters ---------- name : {'torch', 'tensorflow', 'auto'} ``'auto'`` triggers detection and selects the first available framework. persist : bool, default False If ``True``, save the choice to ``~/.pycsamt/config.json`` so it survives across Python sessions. Raises ------ ValueError If *name* is not ``'torch'``, ``'tensorflow'``, or ``'auto'``. ImportError If the requested backend is not installed. Examples -------- >>> from pycsamt.backends import set_backend >>> set_backend('torch') >>> set_backend('tensorflow', persist=True) # saves to config file """ _CFG.set(name) if persist: _CFG.write_config_file(_CFG.backend_name)
[docs] def auto_detect() -> str: """ Detect the best available backend and activate it. Returns ------- name : str The name of the detected and activated backend. Raises ------ RuntimeError If no compatible framework is installed. Examples -------- >>> from pycsamt.backends import auto_detect >>> backend = auto_detect() >>> print(backend) torch """ _CFG.reset() return _CFG.backend_name
[docs] def list_backends() -> dict[str, bool]: """ Return availability status for all known backends. Returns ------- availability : dict ``{'torch': bool, 'tensorflow': bool}`` Examples -------- >>> from pycsamt.backends import list_backends >>> list_backends() {'torch': True, 'tensorflow': False} """ return { "torch": probe_backend("torch"), "tensorflow": probe_backend("tensorflow"), }
[docs] def get_backend_instance() -> Any: """ Return the concrete :class:`~pycsamt.backends._base.NeuralBackend` instance for the active backend, or ``None`` when no DL framework is installed. Returns ------- backend : NeuralBackend or None :class:`~pycsamt.backends._torch.TorchBackend`, :class:`~pycsamt.backends._tensorflow.TensorFlowBackend`, or ``None`` when no framework is available. """ name = get_backend() if name == "torch": from ._torch import TorchBackend return TorchBackend() if name == "tensorflow": from ._tensorflow import TensorFlowBackend return TensorFlowBackend() if name == "none": return None raise RuntimeError(f"Unknown backend {name!r}")