Source code for lbfextract

"""

LBFextract
==========

LBFextract is a Python package designed for extracting features from a BAM file, with a particular emphasis on liquid
biopsy-related features and transcription factors. The package employs a plugin interface where each plugin represents a
feature that can be extracted from a BAM file. It consists of a core package housing the main logic, supplemented by a
collection of plugins encompassing various features. Within the ``lbfextract`` package, the core components include:

- **Core Module:** Describes the workflow and underlying logic.
- **CLI Module:** Constructs the Command-Line Interface (CLI).
- **Feature Extractor:** Builds the Python interface.
- **Hookspecs:** Defines the interface for hooks.
- **Plugin Manager:** Manages the loading of plugins.

Third-party plugins can implement the following hooks:

- **fetch_reads:** Extracts features from a BAM file.
- **load_reads:** Loads reads if previously extracted.
- **save_fetched_reads:** Saves the fetched reads specific to regions of interest.
- **transform_reads:** Applies transformations to each extracted read.
- **transform_single_intervals:** Extracts the signal of a single region.
- **transform_all_intervals:** Applies transformations requiring data from all regions.
- **plot_signal:** Plots the final signal.
- **save_signal:** Saves the final signal.

LBFextract utilizes Pluggy to provide an interface for third-party software. The general interface for hooks, located
in the ``hookspecs.py`` file, outlines the expected signatures for plugins. Default hook implementations are found in
``lib.py`` within the ``fextract`` subpackage.

Additionally, CLI-specific hooks are specified in ``hookspecs.py``, with default implementations found in ``cli_lib.py``
and other subpackage plugin modules. These hooks supply CLI commands to execute workflows associated with each plugin.

For seamless integration with Python, the ``FeatureExtractor`` object allows users to invoke different feature
extraction methods programmatically. Upon installing new plugins containing ``cli_hooks``, the associated commands are
automatically incorporated into the ``FeatureExtractor`` object as accessible methods.

To maintain consistency, each plugin should include a ``schema.py`` file defining the configuration schema for its
feature extraction method. Refer to existing plugins, particularly those generating default coverage and
fragmentomics-based signals, for guidance. Schemas should adhere to the Voluptuous schema syntax.
(`voluptuous documentation <https://pypi.org/project/voluptuous/>`_).


"""

import logging.config
import os
import pathlib
import tempfile
import warnings

import pluggy
import rich_click as click
import yaml
from rich.traceback import install

from lbfextract.setup_conda_env import check_in_conda_env, check_if_conda_env_is_base
from lbfextract.utils_classes import ExternalModuleFilter

install(show_locals=True, suppress=[click, pluggy])

__version__ = "0.1.0a1"

os.environ['MPLCONFIGDIR'] = os.path.join(tempfile.gettempdir(), "matplotlib")

with open(pathlib.Path(__file__).parent / "config" / 'logger_config.yml', 'rt') as f:
    logger_config = yaml.safe_load(f.read())

with open(pathlib.Path(__file__).parent / "config" / 'profiler_config.yml', 'rt') as f:
    PROFILER_DEBUG = yaml.safe_load(f.read())["debug"]

# Configure the logging system
logging.config.dictConfig(logger_config)
root_logger = logging.getLogger()
root_logger.setLevel(logging.INFO)

if PROFILER_DEBUG:
    root_logger.setLevel(logging.DEBUG)


[docs] def custom_warning_handler(message, category, filename, lineno, file=None, line=None): log_message = warnings.formatwarning(message, category, filename, lineno, line) logger.debug(log_message)
warnings.showwarning = custom_warning_handler external_module_filter = ExternalModuleFilter() for handler in root_logger.handlers: handler.addFilter(external_module_filter) logger = logging.getLogger(__name__) hookimpl = pluggy.HookimplMarker("lbfextract") hookimpl_cli = pluggy.HookimplMarker("lbfextract_cli")
[docs] def check_conda_env_was_installed(): env_samtools = None exists = False if check_in_conda_env(): env_samtools = ( pathlib.Path(os.environ.get("CONDA_EXE")).parent.parent / "envs" / "@lbfextract" / "bin" / "samtools" ) exists = env_samtools.exists() if not exists: logger.warning( "@lbfextract conda env was not generated. Run 'lbfextract setup create-conda-envs' to create it. " "LBFextract need samtools from the `@lbfextract` conda environment during feature extraction." ) else: logger.warning(f"To use LBFextract conda must be active. Run `conda activate`") return str(env_samtools) if exists else None
PATH_TO_SAMTOOLS = check_conda_env_was_installed()