From d887b4905a808c5aec427199e75b6c0ffae65512 Mon Sep 17 00:00:00 2001 From: "Tom C (DLS)" <101418278+coretl@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:09:48 +0000 Subject: [PATCH] Add testing (#24) --- .devcontainer/devcontainer.json | 45 +++++++++++++++++++++ .github/workflows/_docs.yml | 4 +- .github/workflows/_tox.yml | 31 ++++++++++++++ .github/workflows/ci.yml | 10 ++++- .gitignore | 72 ++++++++++++++++++++++++++++++++- .gitmodules | 3 ++ .pre-commit-config.yaml | 14 +++++++ .vscode/extensions.json | 5 +++ .vscode/launch.json | 19 +++++++++ .vscode/settings.json | 11 +++++ .vscode/tasks.json | 16 ++++++++ Dockerfile | 4 ++ dev-requirements.txt | 24 +++++++++-- docs/conf.py | 2 +- example | 1 + pyproject.toml | 28 ++++++++++++- tests/test_example.py | 26 ++++++++++++ 17 files changed, 304 insertions(+), 11 deletions(-) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .github/workflows/_tox.yml create mode 100644 .gitmodules create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 Dockerfile create mode 160000 example create mode 100644 tests/test_example.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..97074f1f --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,45 @@ +// For format details, see https://containers.dev/implementors/json_reference/ +{ + "name": "Python 3 Developer Container", + "build": { + "dockerfile": "../Dockerfile", + "target": "developer" + }, + "remoteEnv": { + // Allow X11 apps to run inside the container + "DISPLAY": "${localEnv:DISPLAY}", + }, + "customizations": { + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + "python.defaultInterpreterPath": "/venv/bin/python" + }, + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-python.python", + "github.vscode-github-actions", + "tamasfe.even-better-toml", + "redhat.vscode-yaml", + "ryanluker.vscode-coverage-gutters", + "charliermarsh.ruff" + ] + } + }, + "features": { + // Some default things like git config + "ghcr.io/devcontainers/features/common-utils:2": { + "upgradePackages": false + } + }, + "runArgs": [ + // Allow the container to access the host X11 display and EPICS CA + "--net=host", + // Make sure SELinux does not disable with access to host filesystems like tmp + "--security-opt=label=disable" + ], + // Mount the parent as /workspaces so we can pip install peers as editable + "workspaceMount": "source=${localWorkspaceFolder}/..,target=/workspaces,type=bind", + // After the container is created, install the python project in editable form + "postCreateCommand": "pip install -c dev-requirements.txt -e '.[dev]'" +} \ No newline at end of file diff --git a/.github/workflows/_docs.yml b/.github/workflows/_docs.yml index 49fff6c2..a9d4ede6 100644 --- a/.github/workflows/_docs.yml +++ b/.github/workflows/_docs.yml @@ -8,6 +8,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + submodules: true - name: Install system packages run: sudo apt-get install graphviz @@ -45,7 +47,7 @@ jobs: run: python .github/pages/make_switcher.py --add $DOCS_VERSION ${{ github.repository }} build/switcher.json - name: Publish Docs to gh-pages - if: github.event_name == 'push' && github.actor != 'dependabot[bot]' + if: github.ref_type == 'tag' || github.ref_name == 'main' # We pin to the SHA, not the tag, for security reasons. # https://docs.github.com/en/actions/learn-github-actions/security-hardening-for-github-actions#using-third-party-actions uses: peaceiris/actions-gh-pages@bd8c6b06eba6b3d25d72b7a1767993c0aeee42e7 # v3.9.2 diff --git a/.github/workflows/_tox.yml b/.github/workflows/_tox.yml new file mode 100644 index 00000000..e1f58bfc --- /dev/null +++ b/.github/workflows/_tox.yml @@ -0,0 +1,31 @@ +on: + workflow_call: + inputs: + tox: + type: string + description: What to run under tox + required: true + + +jobs: + run: + runs-on: "ubuntu-latest" + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + + - run: find + + - name: Setup python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install python packages + run: pip install -c dev-requirements.txt -e .[dev] + + - name: Run tox + run: tox -e ${{ inputs.tox }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b534869..5011033c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,12 +9,20 @@ jobs: # TODO: Use the CI straight from the template uses: ./.github/workflows/_check.yml - # TODO: add pre-commit and ctt + # TODO: Use the CI straight from the template + test: + needs: check + if: ${{ ! needs.check.outputs.branch-pr }} + uses: ./.github/workflows/_tox.yml + with: + tox: pre-commit,pytest docs: needs: check if: ${{ ! needs.check.outputs.branch-pr }} uses: ./.github/workflows/_docs.yml + permissions: + contents: write release: if: github.ref_type == 'tag' diff --git a/.gitignore b/.gitignore index a2dd67b8..a37be99b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,71 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +.venv build/ -*.egg-info -.tox/ \ No newline at end of file +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg +**/_version.py + +# 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/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +cov.xml +.pytest_cache/ +.mypy_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# likely venv names +.venv* +venv* + +# further build artifacts +lockfiles/ + +# ruff cache +.ruff_cache/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..33785e63 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "example"] + path = example + url = https://github.com/DiamondLightSource/python-copier-template-example.git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4205af38..a6e42326 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,6 +8,20 @@ repos: - repo: local hooks: + - id: ruff + name: lint with ruff + language: system + entry: ruff check --force-exclude + types: [python] + require_serial: true + + - id: ruff-format + name: format with ruff + language: system + entry: ruff format --force-exclude + types: [python] + require_serial: true + - id: constraints name: check constraints match installed language: system diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..66ad6324 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "ms-vscode-remote.remote-containers", + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..fe2b6dc3 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Debug Unit Test", + "type": "python", + "request": "launch", + "justMyCode": false, + "program": "${file}", + "purpose": [ + "debug-test" + ], + "console": "integratedTerminal", + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..c129d991 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + }, + "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff", + }, +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..946e69d4 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,16 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "Tests, lint and docs", + "command": "tox -p", + "options": { + "cwd": "${workspaceRoot}" + }, + "problemMatcher": [], + } + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..38092471 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +ARG PYTHON_VERSION=3.11 +FROM python:${PYTHON_VERSION} as developer +RUN python -m venv /venv +ENV PATH=/venv/bin:$PATH \ No newline at end of file diff --git a/dev-requirements.txt b/dev-requirements.txt index f1bcb8fb..406de27e 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,11 +1,12 @@ # -# This file is autogenerated by pip-compile with Python 3.9 +# This file is autogenerated by pip-compile with Python 3.11 # by the following command: # # pip-compile --extra=dev --no-annotate --output-file=dev-requirements.txt --strip-extras # accessible-pygments==0.0.4 alabaster==0.7.16 +annotated-types==0.6.0 babel==2.14.0 beautifulsoup4==4.12.3 build==1.0.3 @@ -14,14 +15,19 @@ cfgv==3.4.0 charset-normalizer==3.3.2 click==8.1.7 colorama==0.4.6 +copier==9.1.1 +decorator==5.1.1 distlib==0.3.8 docutils==0.20.1 +dunamai==1.19.0 filelock==3.13.1 +funcy==2.0 identify==2.5.33 idna==3.6 imagesize==1.4.1 -importlib-metadata==7.0.1 +iniconfig==2.0.0 jinja2==3.1.3 +jinja2-ansible-filters==1.3.2 livereload==2.6.3 markdown-it-py==3.0.0 markupsafe==2.1.4 @@ -31,16 +37,25 @@ myst-parser==2.0.0 nodeenv==1.8.0 packaging==23.2 pathlib2==2.3.7.post1 +pathspec==0.12.1 pip-tools==7.3.0 platformdirs==4.1.0 pluggy==1.3.0 +plumbum==1.8.2 pre-commit==3.6.0 +prompt-toolkit==3.0.36 py==1.11.0 +pydantic==2.5.3 +pydantic-core==2.14.6 pydata-sphinx-theme==0.15.2 pygments==2.17.2 pyproject-hooks==1.0.0 +pytest==7.4.4 pyyaml==6.0.1 +pyyaml-include==1.3.2 +questionary==2.0.1 requests==2.31.0 +ruff==0.1.14 six==1.16.0 snowballstemmer==2.2.0 soupsieve==2.5 @@ -54,15 +69,16 @@ sphinxcontrib-htmlhelp==2.0.5 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.7 sphinxcontrib-serializinghtml==1.1.10 -tomli==2.0.1 tornado==6.4 tox==3.28.0 tox-direct==0.4 +treecomp==1.0.0 +typer==0.9.0 typing-extensions==4.9.0 urllib3==2.1.0 virtualenv==20.25.0 +wcwidth==0.2.13 wheel==0.42.0 -zipp==3.17.0 # The following packages are considered to be unsafe in a requirements file: # pip diff --git a/docs/conf.py b/docs/conf.py index fdfa2570..abb50268 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,7 +16,7 @@ project = "python-copier-template" # The full version, including alpha/beta/rc tags. -release = check_output(['git', 'describe', '--always', '--tags']).decode() +release = check_output(["git", "describe", "--always", "--tags"]).decode() # The short X.Y version. if "-" in release: diff --git a/example b/example new file mode 160000 index 00000000..24c2b73b --- /dev/null +++ b/example @@ -0,0 +1 @@ +Subproject commit 24c2b73bfd3899c337eeb27749a4485613638c6f diff --git a/pyproject.toml b/pyproject.toml index 3f78ddfe..31b6a5a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,16 +4,28 @@ version = "0.0" [project.optional-dependencies] dev = [ + "copier", "myst-parser", "pip-tools", "pre-commit", "pydata-sphinx-theme>=0.12", + "pytest", + "ruff", "sphinx-autobuild", "sphinx-copybutton", "sphinx-design", "tox-direct", + "treecomp", ] +[tool.pytest.ini_options] +# Run pytest with all our checkers, and don't spam us with massive tracebacks on error +addopts = """ + --tb=native -vv --doctest-modules --doctest-glob="*.rst" + """ +# Doctest python code in docs, python code in src docstrings, test functions in tests +testpaths = "tests" + # tox must currently be configured via an embedded ini string # See: https://github.com/tox-dev/tox/issues/999 [tool.tox] @@ -21,19 +33,31 @@ legacy_tox_ini = """ [tox] skipsdist=True -[testenv:{pre-commit,docs}] +[testenv:{pre-commit,pytest,docs}] # Don't create a virtualenv for the command, requires tox-direct plugin direct = True passenv = * allowlist_externals = pre-commit + pytest sphinx-build sphinx-autobuild commands = pre-commit: pre-commit run --all-files --show-diff-on-failure {posargs} + pytest: pytest {posargs} docs: sphinx-{posargs:build -EW --keep-going} -T docs build/html """ +[tool.ruff] +src = ["src", "tests"] +line-length = 88 +select = [ + "E", # pycodestyle errors - https://beta.ruff.rs/docs/rules/#error-e + "F", # pyflakes rules - https://beta.ruff.rs/docs/rules/#pyflakes-f + "W", # pycodestyle warnings - https://beta.ruff.rs/docs/rules/#warning-w + "I", # isort - https://docs.astral.sh/ruff/rules/#isort-i +] + [tool.pip-tools] src-files = ["pyproject.toml"] extras = ["dev"] @@ -42,4 +66,4 @@ quiet = true # Make output suitable for use as a constraints file strip-extras = true # Remove annotations as coverage source seems to make false positive in CI -no-annotate = true +annotate = false diff --git a/tests/test_example.py b/tests/test_example.py new file mode 100644 index 00000000..7170a29b --- /dev/null +++ b/tests/test_example.py @@ -0,0 +1,26 @@ +import shutil +from pathlib import Path + +import treecomp +import yaml +from copier import run_copy + +TOP = Path(__file__).absolute().parent.parent +INPUT = TOP / "example" +OUTPUT = TOP / "build" / "example" + + +def test_template(): + shutil.rmtree(OUTPUT, ignore_errors=True) + answers = yaml.safe_load((INPUT / ".copier-answers.yml").read_text()) + run_copy( + src_path=str(TOP), + dst_path=OUTPUT, + data=answers, + vcs_ref="HEAD", + unsafe=True, + ) + # + comparison = treecomp.diff_file_trees(INPUT, OUTPUT, ignore=[".copier-answers.yml"]) + if comparison.diffs: + raise AssertionError(str(comparison))