diff --git a/.github/workflows/test-spras.yml b/.github/workflows/test-spras.yml index db6d1b0f..81311466 100644 --- a/.github/workflows/test-spras.yml +++ b/.github/workflows/test-spras.yml @@ -59,8 +59,12 @@ jobs: # Install spras in the environment using pip shell: bash --login {0} run: pip install . - - name: Log conda environment - # Log conda environment contents + - name: Get pipx + shell: bash --login {0} + run: pip install pipx + - shell: bash --login {0} + run: pipx install . + - name: Log conda environment contents shell: bash --login {0} run: conda list - name: Install Apptainer @@ -100,7 +104,7 @@ jobs: # We enable high parallelization (cores 4) to test our way out of the experienced # race conditions from #268 and #279 # We also enforce strict DAG evaluation to catch DAG problems before they appear as user errors. (#359) - run: snakemake --cores 4 --configfile config/config.yaml --show-failed-logs --strict-dag-evaluation cyclic-graph --strict-dag-evaluation functions --strict-dag-evaluation periodic-wildcards + run: spras run --cores 4 --configfile config/config.yaml --show-failed-logs --strict-dag-evaluation cyclic-graph --strict-dag-evaluation functions --strict-dag-evaluation periodic-wildcards # Run pre-commit checks on source files pre-commit: diff --git a/MANIFEST.in b/MANIFEST.in index 72dcf489..0afcd18f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,4 @@ +include README.md +include LICENSE +include Snakefile include spras/cgroup_wrapper.sh diff --git a/README.md b/README.md index 44895c76..21966070 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ After installing Docker, start Docker before running SPRAS. Once you have activated the conda environment and started Docker, you can run SPRAS with the example Snakemake workflow. From the root directory of the `spras` repository, run the command ``` -snakemake --cores 1 --configfile config/config.yaml +spras run --cores 1 --configfile config/config.yaml ``` This will run the SPRAS workflow with the example config file (`config/config.yaml`) and input files. Output files will be written to the `output` directory. diff --git a/docs/contributing/index.rst b/docs/contributing/index.rst index f47e935b..38774d4c 100644 --- a/docs/contributing/index.rst +++ b/docs/contributing/index.rst @@ -306,7 +306,7 @@ through SPRAS with .. code:: bash - snakemake --cores 1 --configfile config/config.yaml + spras run --cores 1 --configfile config/config.yaml Make sure to run the command inside the ``spras`` conda environment. diff --git a/docs/tutorial/beginner.rst b/docs/tutorial/beginner.rst index a0846666..a50c43c9 100644 --- a/docs/tutorial/beginner.rst +++ b/docs/tutorial/beginner.rst @@ -285,7 +285,7 @@ From the root directory, run the command below from the command line: .. code:: bash - snakemake --cores 1 --configfile config/beginner.yaml + spras run --cores 1 --configfile config/beginner.yaml This command starts the workflow manager that automates all steps defined by SPRAS. It tells Snakemake to use one CPU core and to load @@ -430,7 +430,7 @@ After saving the changes, rerun with: .. code:: bash - snakemake --cores 1 --configfile config/beginner.yaml + spras run --cores 1 --configfile config/beginner.yaml What happens when you run this command -------------------------------------- @@ -600,7 +600,7 @@ After saving the changes, rerun with: .. code:: bash - snakemake --cores 1 --configfile config/beginner.yaml + spras run --cores 1 --configfile config/beginner.yaml What happens when you run this command -------------------------------------- diff --git a/docs/tutorial/intermediate.rst b/docs/tutorial/intermediate.rst index 1055b879..21c1e84a 100644 --- a/docs/tutorial/intermediate.rst +++ b/docs/tutorial/intermediate.rst @@ -542,7 +542,7 @@ From the root directory, run the command below from the command line: .. code:: bash - snakemake --cores 4 --configfile config/intermediate.yaml + spras run --cores 4 --configfile config/intermediate.yaml What happens when you run this command -------------------------------------- @@ -836,7 +836,7 @@ After saving the changes in the configuration file, rerun with: .. code:: bash - snakemake --cores 4 --configfile config/intermediate.yaml + spras run --cores 4 --configfile config/intermediate.yaml What happens when you run this command -------------------------------------- diff --git a/docs/usage.rst b/docs/usage.rst index 9b277806..e4932df6 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -3,14 +3,15 @@ ############# SPRAS is run through `Snakemake `_, -which comes with the SPRAS conda environment. +which comes with the SPRAS conda environment and as a dependency of +SPRAS. To run SPRAS, run the following command inside the ``spras`` directory, specifying a ``config.yaml`` and the number of cores to run SPRAS with: .. code:: bash - snakemake --cores 1 --configfile config.yaml + spras run --cores 1 --configfile config.yaml ********************* Parallelizing SPRAS @@ -25,7 +26,7 @@ To parallelize SPRAS, specify ``--cores`` to be a value higher than .. code:: bash - snakemake --cores 4 --configfile config.yaml + spras run --cores 4 --configfile config.yaml SPRAS also supports high-performance computing with its integration with `HTCondor `_. See :doc:`Running with HTCondor diff --git a/pyproject.toml b/pyproject.toml index bfc602c6..9ad9b8f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,12 @@ dev = [ "Homepage" = "https://github.com/Reed-CompBio/spras" "Issues" = "https://github.com/Reed-CompBio/spras/issues" +[project.entry-points."pipx.run"] +spras = "spras.cli:run" + +[project.scripts] +spras = "spras.cli:run" + [build-system] requires = ["setuptools>=64.0"] build-backend = "setuptools.build_meta" diff --git a/spras/__main__.py b/spras/__main__.py new file mode 100644 index 00000000..16b442b7 --- /dev/null +++ b/spras/__main__.py @@ -0,0 +1,3 @@ +if __name__ == "__main__": + from spras.cli import run + run() diff --git a/spras/cli.py b/spras/cli.py new file mode 100644 index 00000000..eaef4749 --- /dev/null +++ b/spras/cli.py @@ -0,0 +1,53 @@ +import argparse +import itertools +import os +import subprocess +from pathlib import Path + +# https://stackoverflow.com/a/5137509/7589775 +# The file we want, Snakefile, is also included in MANIFEST.in +dir_path = os.path.dirname(os.path.realpath(__file__)) +# we resolve to simplify the path name in errors +snakefile_path = Path(dir_path, "..", "Snakefile").resolve() + +# Removes the very awkwardly phrased "{subcommand1, subcommand2}" from the subcommand help +# from https://stackoverflow.com/a/13429281/7589775 +class SubcommandHelpFormatter(argparse.RawDescriptionHelpFormatter): + def _format_action(self, action): + parts = super(argparse.RawDescriptionHelpFormatter, self)._format_action(action) + if action.nargs == argparse.PARSER: + parts = "\n".join(parts.split("\n")[1:]) + return parts + +def get_parser(): + parser = argparse.ArgumentParser( + prog='SPRAS', + description='The wrapping tool for SPRAS (signaling pathway reconstruction analysis streamliner)', + epilog='SPRAS is in alpha. Report issues or suggest features on GitHub: https://github.com/Reed-CompBio/spras', + formatter_class=SubcommandHelpFormatter) + + subparsers = parser.add_subparsers(title='subcommands', + help='subcommand help', + dest='subcommand') + subparsers = subparsers.add_parser('run', + help='Run the SPRAS Snakemake workflow', + # We let snakemake handle help + add_help=False) + + return parser + +def run(): + parser = get_parser() + (args, unknown_args) = parser.parse_known_args() + + if args.subcommand == "run": + subprocess.run(list(itertools.chain( + ["snakemake", "-s", snakefile_path], + unknown_args + ))) + return + + parser.print_help() + +if __name__ == '__main__': + run()