diff --git a/containers/test-template/test-template.sh b/containers/test-template/test-template.sh index 27dea773..ba73546a 100755 --- a/containers/test-template/test-template.sh +++ b/containers/test-template/test-template.sh @@ -10,14 +10,11 @@ script="'${script_name}' tests/data tests/result participant -c1 --skip-bids-val case "$method" in "setuptools" ) python -m venv .venv - .venv/bin/python -m pip install . - # if [ -d /src ]; then - # .venv/bin/python -m pip install /src - # fi + .venv/bin/python -m pip install --no-color . PATH=".venv/bin:$PATH" eval "$script" ;; "poetry" ) - poetry install + poetry install --no-ansi eval "poetry run $script" ;; "hatch" ) @@ -28,6 +25,12 @@ case "$method" in pdm install eval "pdm run $script" ;; + "docs" ) + python -m venv .venv + .venv/bin/python -m pip install . + .venv/bin/python -m pip install -r docs/requirements.txt + .venv/bin/sphinx-build docs build/docs -W + ;; * ) >&2 echo "Invalid method" exit 1 diff --git a/snakebids/project_template/.gitignore b/snakebids/project_template/.gitignore new file mode 100644 index 00000000..68bc17f9 --- /dev/null +++ b/snakebids/project_template/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/.gitignore b/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/.gitignore deleted file mode 100644 index e35d8850..00000000 --- a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_build diff --git a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/Makefile b/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/Makefile deleted file mode 100644 index d4bb2cbb..00000000 --- a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/conf.py.jinja b/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/conf.py.jinja index ad690830..48dac526 100644 --- a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/conf.py.jinja +++ b/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/conf.py.jinja @@ -30,6 +30,7 @@ author = "{{full_name}}" extensions = [ "sphinx_rtd_theme", "sphinxarg.ext", + "myst_parser", ] # Add any paths that contain templates here, relative to this directory. @@ -54,4 +55,4 @@ html_theme = "sphinx_rtd_theme" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] +# html_static_path = ["_static"] diff --git a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/getting_started/installation.md.jinja b/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/getting_started/installation.md.jinja index 710cc7fe..403bf3c2 100644 --- a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/getting_started/installation.md.jinja +++ b/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/getting_started/installation.md.jinja @@ -1,4 +1,4 @@ -## Installation +# Installation Install from github with pip: @@ -8,7 +8,7 @@ pip install -e git+https://github.com/{{github}}/{{name_slug}}#egg={{name_slug}} Note: you can re-run this command to re-install with the latest version -## Running the app +# Running the app Do a dry-run first (`-n`) and simply print (`-p`) what would be run: @@ -24,7 +24,7 @@ Run the app, using all cores:: If any workflow rules require containers, then run with the `--use-singularity` option. -## Generating a report +# Generating a report After your processing is complete, you can use snakemake's `--report` feature to generate an HTML report. This report will include a graph of all the jobs run, with clickable nodes @@ -38,13 +38,13 @@ To generate a report, run: {{name_slug}} /path/to/bids/dir /path/to/output/dir participant --report ``` -## Compute Canada Instructions +# Compute Canada Instructions -### Setting up a dev environment +## Setting up a dev environment Here are some instructions to get your python environment set-up on graham to run {{name_slug}}: -# Create a virtualenv and activate it: +### Create a virtualenv and activate it: ```bash cd $SCRATCH @@ -53,9 +53,9 @@ virtualenv venv_{{name_slug}} source venv_{{name_slug}}/bin/activate ``` -# Follow the steps above to install from github repository +### Follow the steps above to install from github repository -### Install job submission helpers +## Install job submission helpers Snakemake can submit jobs with SLURM, but you need to set-up a Snakemake profile to enable this. The Khan lab has a snakemake profile that is configured for graham but is customizable upon install, please see `cc-slurm ` for more info. @@ -64,7 +64,7 @@ If you don't need Snakemake to parallelize jobs across different nodes, you can These are used in the instructions below. -### Running jobs on Compute Canada +## Running jobs on Compute Canada In an interactive job (for testing): diff --git a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/index.md b/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/index.md index dd12eea1..d264bf96 100644 --- a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/index.md +++ b/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/index.md @@ -4,7 +4,7 @@ ```{toctree} :maxdepth: 1 -:caption: Contents: +:caption: "Contents:" ``` ```{toctree} diff --git a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/requirements.txt.jinja b/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/requirements.txt.jinja index 4af5df61..c8919d3c 100644 --- a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/requirements.txt.jinja +++ b/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/requirements.txt.jinja @@ -1,4 +1,4 @@ docutils<0.18 sphinx-argparse sphinx_rtd_theme -snakebids=={{ snakebids_version }} +myst-parser diff --git a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/usage/app_cli.md.jinja b/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/usage/app_cli.md.jinja index 4d3625c1..47110725 100644 --- a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/usage/app_cli.md.jinja +++ b/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/usage/app_cli.md.jinja @@ -1,7 +1,6 @@ -## Command line interface +# Command line interface ```{argparse} -:filename: ../{{ name_slug }}/run.py -:func: get_parser +:ref: {{ name_slug }}.run.get_parser :prog: {{ name_slug }} ``` diff --git a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/usage/snakemake_cli.md b/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/usage/snakemake_cli.md index 2e834372..4ebfb83a 100644 --- a/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/usage/snakemake_cli.md +++ b/snakebids/project_template/{% if create_doc_template %}docs{% endif %}/usage/snakemake_cli.md @@ -1,4 +1,4 @@ -## Snakemake Command line interface +# Snakemake Command line interface ```{argparse} :module: snakemake diff --git a/snakebids/project_template/{{name_slug}}/run.py.jinja b/snakebids/project_template/{{name_slug}}/run.py similarity index 80% rename from snakebids/project_template/{{name_slug}}/run.py.jinja rename to snakebids/project_template/{{name_slug}}/run.py index 1b782c1f..e7880887 100755 --- a/snakebids/project_template/{{name_slug}}/run.py.jinja +++ b/snakebids/project_template/{{name_slug}}/run.py @@ -6,12 +6,13 @@ def get_parser(): - """Exposes parser for sphinx doc generation, cwd is the docs dir""" - app = SnakeBidsApp("../{{ name_slug }}") + """Exposes parser for sphinx doc generation, cwd is the docs dir.""" + app = SnakeBidsApp(Path(__file__).resolve().parent) # to get repository root return app.parser def main(): + """Run the app.""" app = SnakeBidsApp( Path(__file__).resolve().parent, # to get repository root plugins=[BidsValidator()], diff --git a/snakebids/tests/helpers.py b/snakebids/tests/helpers.py index cb563be0..8cf16eaf 100644 --- a/snakebids/tests/helpers.py +++ b/snakebids/tests/helpers.py @@ -289,14 +289,18 @@ def decorator(func: Callable[_P, _T]) -> Callable[_P, _T]: @ft.wraps(func) def wrapper(*args: _P.args, **kwargs: _P.kwargs): try: - sp.run(["docker"], check=True) + sp.run(["docker"], check=True, capture_output=True) except (sp.CalledProcessError, FileNotFoundError): pytest.fail( "docker is not available on this machine. To skip docker tests, " "use '-m \"not docker\"'" ) try: - sp.run(["docker", "image", "inspect", container], check=True) + sp.run( + ["docker", "image", "inspect", container], + capture_output=True, + check=True, + ) except sp.CalledProcessError as err: if not ( match := re.match(r"snakebids/([\w\-]+)\:[a-zA-Z0-9\-]+", container) diff --git a/snakebids/tests/test_template.py b/snakebids/tests/test_template.py index 91b10419..193354a4 100644 --- a/snakebids/tests/test_template.py +++ b/snakebids/tests/test_template.py @@ -331,7 +331,44 @@ def test_template_dry_runs_successfully( try: cmd.check_returncode() except Exception: - print(cmd.stdout) - print(cmd.stderr, file=sys.stderr) + print(cmd.stdout.decode()) + print(cmd.stderr.decode(), file=sys.stderr) raise assert "All set" in cmd.stdout.decode() + + +@needs_docker(f"snakebids/test-template:{platform.python_version()}") +def test_template_docs_build(tmp_path: Path, request: pytest.FixtureRequest): + app_name = "snakebids_app" + data = get_empty_data(app_name, "setuptools") + data["create_doc_template"] = True + data["snakebids_version"] = "@ file:///src" + + copier.run_copy( + str(Path(itx.first(snakebids.__path__), "project_template")), + tmp_path / app_name, + data=data, + unsafe=True, + ) + cmd = sp.run( + [ + "docker", + "run", + "-v", + f"{tmp_path / app_name}:/app", + "-v", + f"{request.config.rootpath}:/src", + "--rm", + f"snakebids/test-template:{platform.python_version()}", + "docs", + app_name, + ], + capture_output=True, + check=False, + ) + try: + cmd.check_returncode() + except Exception: + print(cmd.stdout.decode()) + print(cmd.stderr.decode(), file=sys.stderr) + raise