Skip to content

Commit

Permalink
Merge pull request #75 from sudlab/ns-rse/66-parser
Browse files Browse the repository at this point in the history
  • Loading branch information
ns-rse authored Nov 22, 2024
2 parents fc78eb7 + 116bbb2 commit 6ddcd38
Show file tree
Hide file tree
Showing 3 changed files with 261 additions and 4 deletions.
153 changes: 153 additions & 0 deletions isoslam/processing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
"""Functions for Processing."""

import argparse as arg
import sys
from pathlib import Path
from typing import Any

# from isoslam import __version__


def create_parser() -> arg.ArgumentParser:
"""
Create a parser for reading options.
Parser is created with multiple sub-parsers for eading options to run ``isoslam``.
Returns
-------
arg.ArgumentParser
Argument parser.
"""
parser = arg.ArgumentParser(
description="Run various programs related to IsoSLAM. Add the name of the program you wish to run."
)
parser.add_argument(
"--version",
# version=f"Installed version of IsoSlam : {__version__}",
help="Report the installed version of IsoSLAM.",
)
parser.add_argument(
"-c",
"--config-file",
dest="config_file",
type=Path,
required=False,
help="Path to a YAML configuration file.",
)
parser.add_argument(
"-o",
"--output-dir",
dest="output_dir",
type=Path,
required=False,
help="Output directory to write results to.",
)
parser.add_argument(
"-l",
"--log-level",
dest="log_level",
type=str,
required=False,
help="Logging level to use, default is 'info' for verbose output use 'debug'.",
)

subparsers = parser.add_subparsers(title="program", description="Available programs listed below:", dest="program")

# Create sub-parsers for different stages
process_parser = subparsers.add_parser(
"process",
description="Process all files and run all summary plotting and statistics.",
help="Process all files and run all summary plotting and statistics.",
)
process_parser.add_argument(
"-b",
"--bam-file",
dest="bam_file",
type=Path,
required=False,
help="Path to '.bam' file that has undergone read assignment with 'featureCount'.",
)
process_parser.add_argument(
"-g", "--gtf-file", dest="gtf_file", type=Path, required=False, help="Path to '.gtf' transcript assembly file."
)
process_parser.add_argument(
"-d",
"--bed-file",
dest="bed_file",
type=Path,
required=False,
help="Path to '.bed' utron file. Must be bed6 format.",
)
process_parser.add_argument(
"-v", "--vcf-file", dest="vcf_file", type=Path, required=False, help="Path to '.vcf.gz' file."
)
process_parser.set_defaults(func=process)
# Additional parsers for future functionality
# summarize_counts_parser = subparsers.add_parser(
# "summarize",
# description="Summarize counts.",
# help="Summarize counts.",
# )
# summarize_counts_parser.set_defaults(func=summarize_counts)
# plot_conversions_parser = subparsers.add_parser(
# "plot_conversions",
# description="Plot conversions.",
# help="Plot conversions.",
# )
# plot_conversions_parser.set_defaults(func=plot_conversions)
return parser


def process(args: arg.Namespace | None) -> None:
"""
Process a set of files.
Parameters
----------
args : arg.Namespace | None
Arguments function was invoked with.
Returns
-------
None
Function does not return anything.
"""
return


def entry_point(manually_provided_args: list[Any] | None = None, testing: bool = False) -> None | arg.Namespace:
"""
Entry point for all IsoSLAM programs.
Main entry point for running 'isoslam' which allows the different processing, plotting and testing modules to be
run.
Parameters
----------
manually_provided_args : None
Manually provided arguments.
testing : bool
Whether testing is being carried out.
Returns
-------
None
Function does not return anything.
"""
parser = create_parser()
args = parser.parse_args() if manually_provided_args is None else parser.parse_args(manually_provided_args)

# If no module has been specified print help and exit
print(f"{args.program=}")
if not args.program:
parser.print_help()
sys.exit()

if testing:
return args

# Run the specified module(s)
args.func(args)

return None
10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ name = "isoslam"
description = "Tools for processing and summarising SLAMSeq experiments."
readme = "README.md"
license = {text = "MIT License"}
requires-python = ">=3.9"
requires-python = ">=3.10"
dynamic = ["version"]
authors = [
{name = "Jack Riley", email = "[email protected]"},
Expand All @@ -19,7 +19,6 @@ authors = [
]
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
Expand Down Expand Up @@ -268,7 +267,7 @@ files = [
"isoslam",
"tests"
]
python_version = "3.9"
python_version = "3.10"
strict = true
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
warn_unreachable = true
Expand All @@ -278,4 +277,7 @@ exclude = [

[[tool.mypy.overrides]]
module = [ "numpy.*", ]
ignore_missing_imports = true
ignore_missing_imports = true

[project.scripts]
isoslam = "isoslam.processing:entry_point"
102 changes: 102 additions & 0 deletions tests/test_processing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""Tests of the processing modules entry point, argument parsing and ability to correctly select programs."""

from collections.abc import Callable
from pathlib import Path

import pytest

from isoslam import processing


@pytest.mark.parametrize(
("option", "help_message"),
[
pytest.param(["-h"], "usage:", id="Single letter help"),
pytest.param(["--help"], "program", id="Multiple letter help"),
pytest.param(
["process"],
"Process all files and run all summary plotting and statistics.",
id="process module",
marks=pytest.mark.xfail(reason="work in progress"),
),
],
)
def test_entry_point_help(option: str, help_message: str, capsys: pytest.CaptureFixture) -> None:
"""Test the entry_point()'s help argument."""
try:
processing.entry_point(manually_provided_args=option)
except SystemExit:
pass
output = capsys.readouterr().out
assert help_message in output


@pytest.mark.parametrize(
("options", "expected_function", "expected_args"),
[
pytest.param(
["--config", "dummy/config/dir/config.yaml", "process"],
processing.process,
{"config_file": Path("dummy/config/dir/config.yaml")},
id="dummy config and process",
),
pytest.param(
["--config", "dummy/config/dir/config.yaml", "process", "--bam-file", "data/bam/some.bam"],
processing.process,
{"config_file": Path("dummy/config/dir/config.yaml"), "bam_file": Path("data/bam/some.bam")},
id="dummy config, bam file and process",
),
pytest.param(
[
"--config",
"dummy/config/dir/config.yaml",
"--output-dir",
"output",
"process",
"--bam-file",
"data/bam/some.bam",
],
processing.process,
{
"config_file": Path("dummy/config/dir/config.yaml"),
"output_dir": Path("output"),
"bam_file": Path("data/bam/some.bam"),
},
id="dummy config and output, process with bam file",
),
pytest.param(
[
"--config",
"dummy/config/dir/config.yaml",
"--output-dir",
"output",
"process",
"--bam-file",
"data/bam/some.bam",
"--gtf-file",
"data/gtf/some.gtf",
"--bed-file",
"data/bed/some.bed",
"--vcf-file",
"data/vcf/some.vcf.gz",
],
processing.process,
{
"config_file": Path("dummy/config/dir/config.yaml"),
"output_dir": Path("output"),
"bam_file": Path("data/bam/some.bam"),
"gtf_file": Path("data/gtf/some.gtf"),
"bed_file": Path("data/bed/some.bed"),
"vcf_file": Path("data/vcf/some.vcf.gz"),
},
id="dummy config and output, process with all files",
),
],
)
def test_entry_point_sub_parsers(options: str, expected_function: Callable, expected_args: dict) -> None:
"""Test the correct function is returned by each subparser."""
returned_args = processing.entry_point(manually_provided_args=options, testing=True)
assert returned_args.func == expected_function
returned_args_dict = vars(returned_args)
for argument, value in expected_args.items():
assert returned_args_dict[argument] == value

0 comments on commit 6ddcd38

Please sign in to comment.