Skip to content

Configuration API

SeqNado uses Pydantic for its configuration management. This page documents the primary configuration models.

Main Configuration

seqnado.config.SeqnadoConfig

Bases: BaseModel

Configuration for the SeqNado workflow.

organism property

organism: str

Return the organism (string) from the genome configuration.

shift_for_tn5_insertion property

shift_for_tn5_insertion: bool

Return the Tn5 shift configuration for the specified assay.

mcc_viewpoints property

mcc_viewpoints: str

Return the MCC viewpoints file path.

set_default_pcr_duplicates

set_default_pcr_duplicates(values)

Set default PCR duplicate handling based on assay type.

Source code in seqnado/config/core.py
@model_validator(mode="before")
def set_default_pcr_duplicates(cls, values):
    """Set default PCR duplicate handling based on assay type."""
    from seqnado import PCRDuplicateHandling

    if "pcr_duplicates" not in values or values["pcr_duplicates"] is None:
        assay = values.get("assay")
        # Normalize assay to Assay enum if it's a string
        if isinstance(assay, str):
            assay = Assay(assay)
        # Default to REMOVE for ATAC, ChIP, CAT, SNP, and METH; KEEP for RNA
        if assay in [Assay.ATAC, Assay.CHIP, Assay.CAT, Assay.SNP, Assay.METH]:
            values["pcr_duplicates"] = PCRDuplicatesConfig(strategy=PCRDuplicateHandling.REMOVE)
        else:
            values["pcr_duplicates"] = PCRDuplicatesConfig(strategy=PCRDuplicateHandling.NONE)

    return values

migrate_remove_blacklist_to_qc

migrate_remove_blacklist_to_qc(values)

Support legacy top-level remove_blacklist by moving it under qc.

Source code in seqnado/config/core.py
@model_validator(mode="before")
def migrate_remove_blacklist_to_qc(cls, values):
    """Support legacy top-level remove_blacklist by moving it under qc."""
    if not isinstance(values, dict):
        return values

    if "remove_blacklist" in values:
        qc_values = dict(values.get("qc") or {})
        qc_values.setdefault("remove_blacklist", values.pop("remove_blacklist"))
        values["qc"] = qc_values

    return values

sync_peak_caller_tool_defaults

sync_peak_caller_tool_defaults() -> SeqnadoConfig

Ensure peak-caller-specific tool configs exist when those methods are selected.

Source code in seqnado/config/core.py
@model_validator(mode="after")
def sync_peak_caller_tool_defaults(self) -> "SeqnadoConfig":
    """Ensure peak-caller-specific tool configs exist when those methods are selected."""
    peak_calling = getattr(self.assay_config, "peak_calling", None)
    methods = getattr(peak_calling, "method", None) or []

    if self.third_party_tools is None:
        bigwigs = getattr(self.assay_config, "bigwigs", None)
        binsize = getattr(bigwigs, "binsize", None)
        self.third_party_tools = ThirdPartyToolsConfig.for_assay(self.assay, binsize=binsize)

    if PeakCallingMethod.SEACR in methods and self.third_party_tools.seacr is None:
        self.third_party_tools.seacr = Seacr()

    return self

require_bamnado_for_per_group_scaling

require_bamnado_for_per_group_scaling() -> SeqnadoConfig

Bamnado computes all per-group-technique scale factors (csaw-background/tmm/ median-of-ratios/cpm) via bam-normalize — there is no hand-rolled fallback.

Source code in seqnado/config/core.py
@model_validator(mode="after")
def require_bamnado_for_per_group_scaling(self) -> "SeqnadoConfig":
    """Bamnado computes all per-group-technique scale factors (csaw-background/tmm/
    median-of-ratios/cpm) via `bam-normalize` — there is no hand-rolled fallback.
    """
    bigwigs = getattr(self.assay_config, "bigwigs", None)
    scale_methods = getattr(bigwigs, "scale_methods", None) or []

    if DataScalingTechnique.PER_GROUP.value in scale_methods:
        bamnado = getattr(self.third_party_tools, "bamnado", None)
        if bamnado is None:
            raise UserFriendlyError(
                f"bigwigs.scale_methods includes '{DataScalingTechnique.PER_GROUP.value}', which requires "
                "bamnado to compute scale factors. Configure third_party_tools.bamnado (e.g. "
                f"via `seqnado tools install bamnado`) or remove '{DataScalingTechnique.PER_GROUP.value}' "
                "from scale_methods."
            )

    return self

validate_qc_remove_blacklist

validate_qc_remove_blacklist() -> SeqnadoConfig

Require a genome blacklist when blacklist removal is enabled.

Source code in seqnado/config/core.py
@model_validator(mode="after")
def validate_qc_remove_blacklist(self) -> "SeqnadoConfig":
    """Require a genome blacklist when blacklist removal is enabled."""
    if self.qc.remove_blacklist and not self.genome.blacklist:
        raise ValueError(
            "qc.remove_blacklist can only be True if genome blacklist is provided."
        )
    return self

from_yaml classmethod

from_yaml(path: Path) -> SeqnadoConfig

Load configuration from a YAML file.

Source code in seqnado/config/core.py
@classmethod
def from_yaml(cls, path: Path) -> "SeqnadoConfig":
    """Load configuration from a YAML file."""
    import yaml

    with open(path, "r") as f:
        data = yaml.safe_load(f)

    return cls(**data)

validate_assay_config_matches_assay

validate_assay_config_matches_assay(v, info)

Ensure the assay_config type matches the specified assay.

Source code in seqnado/config/core.py
@field_validator("assay_config", mode="before")
def validate_assay_config_matches_assay(cls, v, info):
    """Ensure the assay_config type matches the specified assay."""
    if v is None:
        return v

    assay = info.data.get("assay")
    if assay is None:
        return v

    expected_config_class = ASSAY_CONFIG_MAP.get(assay)
    if expected_config_class and not isinstance(v, expected_config_class):
        if isinstance(v, dict):
            # Try to create the appropriate config from dict
            return expected_config_class(**v)
        else:
            raise ValueError(
                f"assay_config must be of type {expected_config_class.__name__} for assay {assay.value}"
            )

    return v

create_assay_config classmethod

create_assay_config(
    assay: Assay, **kwargs
) -> AssaySpecificConfig

Create the appropriate assay config for the given assay type.

Source code in seqnado/config/core.py
@classmethod
def create_assay_config(cls, assay: Assay, **kwargs) -> AssaySpecificConfig:
    """Create the appropriate assay config for the given assay type."""
    config_class = cls._ASSAY_CONFIG_MAP.get(assay)
    if config_class is None:
        raise ValueError(
            f"No configuration class available for assay {assay.value}"
        )

    return config_class(**kwargs)

seqnado.config.ProjectConfig

Bases: BaseModel

Configuration for the SeqNado project.

Genome Configuration

seqnado.config.GenomeConfig

Bases: BaseModel

Configuration for genome-related files and indices.

Assay Specific Configurations

seqnado.config.ATACAssayConfig

Bases: BaseAssayConfig, PeakCallingMixin

Configuration specific to ATAC-seq assays.

validate_spikein_methods classmethod

validate_spikein_methods(v)

Filter out incompatible spike-in methods for ATAC-seq.

ATAC-seq spike-in normalisation only supports ORLANDO (per-sample spike-in read count normalisation). WITH_INPUT requires paired input/control samples (a ChIP-seq concept ATAC-seq doesn't have), and DESEQ2/EDGER are gene-count based methods intended for RNA-seq.

Source code in seqnado/config/core.py
@field_validator("spikein")
@classmethod
def validate_spikein_methods(cls, v):
    """Filter out incompatible spike-in methods for ATAC-seq.

    ATAC-seq spike-in normalisation only supports ORLANDO (per-sample spike-in
    read count normalisation). WITH_INPUT requires paired input/control samples
    (a ChIP-seq concept ATAC-seq doesn't have), and DESEQ2/EDGER are gene-count
    based methods intended for RNA-seq.
    """
    if v is not None:
        disallowed = [m for m in v.method if m != SpikeInMethod.ORLANDO]
        if disallowed:
            from seqnado.utils import warn_once
            warn_once(
                f"The following spike-in method(s) are not compatible with ATAC-seq "
                f"and will be skipped: {[m.value for m in disallowed]}. "
                "ATAC-seq spike-in normalisation supports only 'orlando'."
            )
            v.method = [m for m in v.method if m == SpikeInMethod.ORLANDO]
    return v

seqnado.config.ChIPAssayConfig

Bases: BaseAssayConfig, PeakCallingMixin

Configuration specific to ChIP-seq assays.

seqnado.config.RNAAssayConfig

Bases: BaseAssayConfig

Configuration specific to RNA-seq assays.

validate_spikein_methods classmethod

validate_spikein_methods(v)

Filter out incompatible spike-in methods for RNA-seq.

WITH_INPUT requires paired input/control samples, which is a ChIP-seq concept. RNA-seq supports only ORLANDO, DESEQ2, and EDGER.

Source code in seqnado/config/core.py
@field_validator("spikein")
@classmethod
def validate_spikein_methods(cls, v):
    """Filter out incompatible spike-in methods for RNA-seq.

    WITH_INPUT requires paired input/control samples, which is a ChIP-seq concept.
    RNA-seq supports only ORLANDO, DESEQ2, and EDGER.
    """
    if v is not None and SpikeInMethod.WITH_INPUT in v.method:
        from seqnado.utils import warn_once
        warn_once(
            "The 'with_input' spike-in method is not compatible with RNA-seq and will be skipped. "
            "RNA-seq supports: 'orlando', 'deseq2', 'edger'. "
            "WITH_INPUT requires paired input/control samples (ChIP-seq concept)."
        )
        # Filter out the incompatible method
        v.method = [m for m in v.method if m != SpikeInMethod.WITH_INPUT]
    return v

validate_scale_methods classmethod

validate_scale_methods(v)

Filter out the per-group scaling technique for RNA-seq.

Bamnado library-scaling assumes roughly-equal signal across bins and is inappropriate for RNA-seq's compositional read-count bias (a handful of highly-expressed genes dominate). RNA-seq should use spike-in (orlando/ deseq2/edger) normalisation instead.

Source code in seqnado/config/core.py
@field_validator("bigwigs")
@classmethod
def validate_scale_methods(cls, v):
    """Filter out the per-group scaling technique for RNA-seq.

    Bamnado library-scaling assumes roughly-equal signal across bins and
    is inappropriate for RNA-seq's compositional read-count bias (a handful of
    highly-expressed genes dominate). RNA-seq should use spike-in (orlando/
    deseq2/edger) normalisation instead.
    """
    if v is not None and v.scale_methods and DataScalingTechnique.PER_GROUP.value in v.scale_methods:
        from seqnado.utils import warn_once
        warn_once(
            f"The '{DataScalingTechnique.PER_GROUP.value}' scaling technique is not appropriate for "
            "RNA-seq (compositional read-count bias) and will be skipped. Use spike-in normalisation "
            "('orlando', 'deseq2', 'edger') instead."
        )
        v.scale_methods = [m for m in v.scale_methods if m != DataScalingTechnique.PER_GROUP.value]
    return v

← Back to API Overview