This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
# Install dev dependencies
uv sync --extra dev --extra all
# Run all tests with coverage
uv run pytest tests/
# Run a single test file
uv run pytest tests/test_seq_extract.py
# Run a single test function
uv run pytest tests/test_seq_extract.py -k "test_extract_cds"
# Lint
uv run ruff check src tests
# Format
uv run black src/ tests/
# Type check
uv run mypy src/
# Build package
uv buildjsrc is a CLI bioinformatics toolkit with 7 modules. Entry point: jsrc = "jsrc.cli:main". The package uses a src/ layout with setuptools (where = ["src"]).
_iter_enabled_modules()filters theMODULESdict byJSRC_MODULES/JSRC_DISABLE_MODULESenv vars and disablesjobon Windows._probe_route(argv)parsescommandandsubcommandpositional args to decide what to import.- If the requested module is known,
_register_one_module()imports only that module's top-level package and calls itsregister_subparser(). If the subcommand is also known, only that subcommand module is imported; otherwise stub parsers are registered for all subcommands. - After parsing,
args.func(args)dispatches to the subcommand'scmd(). - The
--debugflag re-raises exceptions with full traceback; without it, exceptions are caught and formatted asError: <type> - <message>.
Each module's __init__.py must contain:
_SUBCOMMANDS: a dict mapping subcommand name →("full.module.path", "help text"). Example:_SUBCOMMANDS: dict[str, tuple[str, str]] = { "extract": ("jsrc.seq.extract", "Extract sequences by IDs"), "qc": ("jsrc.seq.qc", "Sequence quality statistics"), }
register_subparser(subparsers, selected_subcommand=None): creates a subparser for the module, optionally loads only the selected subcommand, otherwise registers stubs for all entries in_SUBCOMMANDS._register_stub_subcommands(subparsers): registers a bareadd_parser(name, help=...)for every subcommand (no args — just enough for--help)._register_selected_subcommand(subparsers, selected): imports the specific subcommand module and calls itsregister().parser.set_defaults(_group_parser=parser): set on the module-level parser so the CLI can print module help when no subcommand is given.
Every subcommand module (e.g., seq/extract.py) exposes two functions:
register(subparsers)— adds a subparser with arguments and must callp.set_defaults(func=cmd)to wire the command function.cmd(args: Namespace)— the actual command implementation.
| module | package | notes |
|---|---|---|
seq |
jsrc.seq |
Sequence extraction, k-mer, translation, alignment, digestion |
genome |
jsrc.genome |
Genome stats, CpG islands, ORF finding, ANI, Ka/Ks, codon usage |
plot |
jsrc.plot |
Gene/exon/chromosome/dotplot/circos diagrams (requires matplotlib) |
analyze |
jsrc.analyze |
Phylogeny, MSA consensus, SNP/INDEL, motif discovery |
grn |
jsrc.grn |
Gene regulatory network conversion, centrality, local viewer |
vision |
jsrc.vision |
Image object extraction, EFD, morphology traits (requires opencv) |
job |
jsrc.job |
Background job submit/monitor/log/kill (Linux/macOS only, disabled on Windows) |
The toolkit deliberately keeps a small command surface: when a new capability overlaps an existing command, it is merged into a flag on that command rather than spawning a new subcommand. Do not reintroduce the removed forms. Current consolidations:
analyze phylo -n/--bootstrap N -seed S— branch support values; formerly the separateanalyze bootstrap_phylogenome codon --cai ref [--per-gene]— one global CAI, or a per-gene CAI table with--per-gene; formerly the separategenome caigenome window --cumulative— cumulative GC skew + replication-origin prediction; formerly the separategenome gc-skew- QC is
analyze qconly (FASTA/FASTQ/SAM/VCF) — there is noseq qc plothas noheart/rosecommands (removed)
All custom exceptions live in src/jsrc/core.py and are sibling subclasses of JsrcError (a flat family — each inherits directly from JsrcError, not a chain): ValidationError, DataFormatError, ResourceNotFoundError, DependencyError, ConfigurationError.
The CLI's main() catches each in its own except branch and prints a type-specific line to stderr, e.g. Error: Invalid input - <message> (ValidationError), Error: Resource not found - (ResourceNotFoundError), Error: Data format error - (DataFormatError), Error: External dependency error - (DependencyError), Error: Configuration error - (ConfigurationError). ValueError → Error: Invalid value - and PermissionError → Error: Permission denied - are also caught; any other Exception becomes Error: Unexpected error - <message>. Error exits use code 2 (130 for KeyboardInterrupt, 1 when no command is given). Subcommands should raise JsrcError subclasses (not SystemExit). The --debug flag re-raises so the full traceback prints.
load_fasta(path)— parse FASTA with Biopython, raisesDataFormatErrorif emptyopen_text(path)— open text files, transparently handles.gz(gzip)check_input(path, label=None)— returnPath(path)if it exists, else raiseResourceNotFoundError; use this for all input-file checks (notFileNotFoundError)parse_gff_attributes(attr_string)— parse GFF/GTF attribute column into dict (URL-unescapes GFF3 values like%3B)setup_matplotlib()— configure Agg backend for headless plotting; call this at the top ofcmd()in any subcommand that uses matplotlibprogressbar— context manager / iterable wrapper for stderr progress bars; usewith progressbar(total=N, desc="...") as pb:orfor item in pb.iter(items):nxx(lengths, pct)— compute N50/N90-style metrics
Subcommands in plot and vision (and any that need optional extras) should:
- Call
setup_matplotlib()at the top ofcmd()(for matplotlib). - Import optional libraries (e.g.,
cv2) inline insidecmd(). - Raise
DependencyErrorwith a clear install hint if the import fails:try: import cv2 except ImportError: raise DependencyError("opencv-python is required. Install with: pip install jsrc[vision]")
Tests live in tests/ and mirror the module structure (e.g., tests/test_seq_extract.py for src/jsrc/seq/extract.py). There are no committed fixture files — every test builds its inputs on the fly with pytest's tmp_path plus inline strings, so the data-definition lives at the top of each test function (don't look for a test/ data directory; the test/... paths in the README are illustrative examples, not real files). tests/conftest.py adds src/ to sys.path. Coverage is enforced on every run via addopts = "--cov=jsrc ..." in pyproject.toml, so uv run pytest always emits htmlcov/, coverage.xml, and .coverage.
.github/workflows/ci.yml — lint (ruff + black) on ubuntu; test matrix: [ubuntu, macos, windows] × [3.10, 3.11, 3.12, 3.13], plus uv build on every test job. .github/workflows/publish.yml — PyPI publish on v* tags via trusted publishing.
JSRC_MODULES— comma-separated list of modules to enable (whitelist)JSRC_DISABLE_MODULES— comma-separated list of modules to disableJSRC_JOBS_FILE— override path for job history file
- The
jobmodule uses/procon Linux for RSS metrics; on macOS it falls back tops. It is automatically disabled on Windows. plotandvisionmodules have optional dependencies (matplotlib, opencv-python). Subcommands that need them should callsetup_matplotlib()or import opencv inline and raiseDependencyErrorwith a clear install hint if missing.grnandplotmodules package static assets (HTML/CSS/JS insources/) via[tool.setuptools.package-data]inpyproject.toml.