From 2df7d77ecd63f21f6fa54489302347668d09dfc7 Mon Sep 17 00:00:00 2001 From: Sean Hammond Date: Sat, 9 Jul 2022 11:30:35 +0100 Subject: [PATCH] Initial commit --- .cookiecutter/cookiecutter.json | 20 +++ .github/dependabot.yml | 9 ++ .github/workflows/ci.yml | 75 ++++++++++ .github/workflows/pypi.yml | 8 ++ .gitignore | 12 ++ .python-version | 3 + HACKING.md | 102 +++++++++++++ INSTALL.md | 39 +++++ LICENSE | 25 ++++ Makefile | 86 +++++++++++ README.md | 14 ++ bin/make_python | 18 +++ bin/make_template | 25 ++++ pyproject.toml | 135 ++++++++++++++++++ setup.cfg | 42 ++++++ src/cookiecutter_pypackage_test/__init__.py | 0 src/cookiecutter_pypackage_test/main.py | 18 +++ tests/__init__.py | 0 tests/functional/__init__.py | 0 .../cookiecutter_pypackage_test_test.py | 2 + tests/pyproject.toml | 92 ++++++++++++ tests/unit/__init__.py | 0 .../cookiecutter_pypackage_test/__init__.py | 0 .../cookiecutter_pypackage_test/main_test.py | 6 + tox.ini | 49 +++++++ 25 files changed, 780 insertions(+) create mode 100644 .cookiecutter/cookiecutter.json create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/pypi.yml create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 HACKING.md create mode 100644 INSTALL.md create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100755 bin/make_python create mode 100755 bin/make_template create mode 100644 pyproject.toml create mode 100644 setup.cfg create mode 100644 src/cookiecutter_pypackage_test/__init__.py create mode 100644 src/cookiecutter_pypackage_test/main.py create mode 100644 tests/__init__.py create mode 100644 tests/functional/__init__.py create mode 100644 tests/functional/cookiecutter_pypackage_test_test.py create mode 100644 tests/pyproject.toml create mode 100644 tests/unit/__init__.py create mode 100644 tests/unit/cookiecutter_pypackage_test/__init__.py create mode 100644 tests/unit/cookiecutter_pypackage_test/main_test.py create mode 100644 tox.ini diff --git a/.cookiecutter/cookiecutter.json b/.cookiecutter/cookiecutter.json new file mode 100644 index 0000000..d02ccea --- /dev/null +++ b/.cookiecutter/cookiecutter.json @@ -0,0 +1,20 @@ +{ + "template": "https://github.com/hypothesis/cookiecutters", + "directory": "pypackage", + "ignore": [], + "extra_context": { + "name": "cookiecutter-pypackage-test", + "package_name": "cookiecutter_pypackage_test", + "slug": "cookiecutter-pypackage-test", + "short_description": "A test package for our pypackage cookiecutter.", + "python_versions": "3.10.4, 3.9.12, 3.8.13", + "github_owner": "hypothesis", + "copyright_holder": "Hypothesis", + "public": "no", + "console_script": "yes", + "__dependabot_interval": "weekly", + "__entry_point": "cookiecutter-pypackage-test", + "__github_url": "https://github.com/hypothesis/cookiecutter-pypackage-test", + "__pypi_url": "https://pypi.org/project/cookiecutter-pypackage-test" + } +} \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6c2ba5f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: +- package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + day: "sunday" + time: "00:00" + timezone: "Europe/London" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..49ef0ca --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,75 @@ +name: CI +on: + pull_request: + workflow_dispatch: + schedule: + - cron: '0 1 * * *' +jobs: + Format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + - run: python -m pip install tox + - run: tox -e checkformatting + Lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + - run: python -m pip install tox + - run: tox -e lint + Tests: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.10', '3.9', '3.8'] + name: Unit tests with Python ${{ matrix.python-version }} + steps: + - uses: actions/checkout@v3 + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - run: python -m pip install tox + - run: tox -e tests + - name: Upload coverage file + uses: actions/upload-artifact@v3 + with: + name: coverage + path: .coverage.* + Coverage: + needs: tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + - name: Download coverage files + uses: actions/download-artifact@v3 + with: + name: coverage + - run: python -m pip install tox + - run: tox -e coverage + Functests: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.10', '3.9', '3.8'] + name: Functional tests with Python ${{ matrix.python-version }} + steps: + - uses: actions/checkout@v3 + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - run: python -m pip install tox + - run: tox -e functests diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml new file mode 100644 index 0000000..fb38e52 --- /dev/null +++ b/.github/workflows/pypi.yml @@ -0,0 +1,8 @@ +name: PyPI +on: + release: + types: [published] +jobs: + PyPI: + uses: hypothesis/workflows/.github/workflows/pypi.yml@main + secrets: inherit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9709d35 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +build +coverage +node_modules +yarn-error.log +.coverage* +.tox +*.egg-info +*.pyc +supervisord.log +supervisord.pid +.DS_Store +.devdata.env diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..a71f7d1 --- /dev/null +++ b/.python-version @@ -0,0 +1,3 @@ +3.10.4 +3.9.12 +3.8.13 diff --git a/HACKING.md b/HACKING.md new file mode 100644 index 0000000..1d6a494 --- /dev/null +++ b/HACKING.md @@ -0,0 +1,102 @@ +# Setting up Your cookiecutter-pypackage-test Development Environment + +First you'll need to install: + +* [Git](https://git-scm.com/). + On Ubuntu: `sudo apt install git`, on macOS: `brew install git`. +* [GNU Make](https://www.gnu.org/software/make/). + This is probably already installed, run `make --version` to check. +* [pyenv](https://github.com/pyenv/pyenv). + Follow the instructions in pyenv's README to install it. + The **Homebrew** method works best on macOS. + The **Basic GitHub Checkout** method works best on Ubuntu. + You _don't_ need to set up pyenv's shell integration ("shims"), you can + [use pyenv without shims](https://github.com/pyenv/pyenv#using-pyenv-without-shims). + +Then to set up your development environment: + +```terminal +git clone https://github.com/hypothesis/cookiecutter-pypackage-test.git +cd cookiecutter_pypackage_test +make help +``` + +Releasing a New Version of the Project +-------------------------------------- + +1. First, to get PyPI publishing working you need to go to: + + and add cookiecutter-pypackage-test to the `PYPI_TOKEN` secret's selected + repositories. + +2. Now that the cookiecutter-pypackage-test project has access to the `PYPI_TOKEN` secret + you can release a new version by just [creating a new GitHub release](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository). + Publishing a new GitHub release will automatically trigger + [a GitHub Actions workflow](.github/workflows/pypi.yml) + that will build the new version of your Python package and upload it to + . + +Changing the Project's Python Versions +-------------------------------------- + +To change what versions of Python the project uses: + +1. Change the Python versions in the + [cookiecutter.json](.cookiecutter/cookiecutter.json) file. For example: + + ```json + "python_versions": "3.10.4, 3.9.12", + ``` + +2. Re-run the cookiecutter template: + + ```terminal + make template + ``` + +3. Commit everything to git and send a pull request + +Changing the Project's Python Dependencies +------------------------------------------ + +To change the production dependencies in the `setup.cfg` file: + +1. Change the dependencies in the [`.cookiecutter/includes/setuptools/install_requires`](.cookiecutter/includes/setuptools/install_requires) file. + If this file doesn't exist yet create it and add some dependencies to it. + For example: + + ``` + pyramid + sqlalchemy + celery + ``` + +2. Re-run the cookiecutter template: + + ```terminal + make template + ``` + +3. Commit everything to git and send a pull request + +To change the project's formatting, linting and test dependencies: + +1. Change the dependencies in the [`.cookiecutter/includes/tox/deps`](.cookiecutter/includes/tox/deps) file. + If this file doesn't exist yet create it and add some dependencies to it. + Use tox's [factor-conditional settings](https://tox.wiki/en/latest/config.html#factors-and-factor-conditional-settings) + to limit which environment(s) each dependency is used in. + For example: + + ``` + lint: flake8, + format: autopep8, + lint,tests: pytest-faker, + ``` + +2. Re-run the cookiecutter template: + + ```terminal + make template + ``` + +3. Commit everything to git and send a pull request diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..d8fec49 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,39 @@ +# Installing cookiecutter-pypackage-test + +We recommend using [pipx](https://pypa.github.io/pipx/) to install +cookiecutter-pypackage-test. +First [install pipx](https://pypa.github.io/pipx/#install-pipx) then run: + +```terminal +pipx install cookiecutter-pypackage-test +``` + +You now have cookiecutter-pypackage-test installed! For some help run: + +``` +cookiecutter-pypackage-test --help +``` + +Upgrading +--------- + +To upgrade to the latest version run: + +```terminal +pipx upgrade cookiecutter-pypackage-test +``` + +To see what version you have run: + +```terminal +cookiecutter-pypackage-test --version +``` + +Uninstalling +------------ + +To uninstall run: + +``` +pipx uninstall cookiecutter-pypackage-test +``` diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3741268 --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2022, Hypothesis +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1962c05 --- /dev/null +++ b/Makefile @@ -0,0 +1,86 @@ +comma := , + +.PHONY: help +help = help::; @echo $$$$(tput bold)$(strip $(1)):$$$$(tput sgr0) $(strip $(2)) +$(call help,make help,print this help message) + +.PHONY: shell +$(call help,make shell,"launch a Python shell in this project's virtualenv") +shell: python + @pyenv exec tox -qe dev + +.PHONY: lint +$(call help,make lint,"lint the code and print any warnings") +lint: python + @pyenv exec tox -qe lint + +.PHONY: format +$(call help,make format,"format the code") +format: python + @pyenv exec tox -qe format + +.PHONY: checkformatting +$(call help,make checkformatting,"crash if the code isn't correctly formatted") +checkformatting: python + @pyenv exec tox -qe checkformatting + +.PHONY: test +$(call help,make test,"run the unit tests in Python 3.10") +coverage: test +test: python + @pyenv exec tox -qe tests + +.PHONY: test-py39 +$(call help,make test-py39,"run the unit tests in Python 3.9") +coverage: test-py39 +test-py39: python + @pyenv exec tox -qe py39-tests + +.PHONY: test-py38 +$(call help,make test-py38,"run the unit tests in Python 3.8") +coverage: test-py38 +test-py38: python + @pyenv exec tox -qe py38-tests + +.PHONY: coverage +$(call help,make coverage,"run the tests and print the coverage report") +coverage: python + @pyenv exec tox -qe coverage + +.PHONY: functests +$(call help,make functests,"run the functional tests in Python 3.10") +functests: python + @pyenv exec tox -qe functests + +.PHONY: functests-py39 +$(call help,make functests-py39,"run the functional tests in Python 3.9") +functests-py39: python + @pyenv exec tox -qe py39-functests + +.PHONY: functests-py38 +$(call help,make functests-py38,"run the functional tests in Python 3.8") +functests-py38: python + @pyenv exec tox -qe py38-functests + +.PHONY: sure +$(call help,make sure,"make sure that the formatting$(comma) linting and tests all pass") +sure: + @pyenv exec tox --parallel -qe 'checkformatting,lint,tests,py{39,38}-tests,coverage,functests,py{39,38}-functests' + +.PHONY: template +$(call help,make template,"update from the latest cookiecutter template") +template: python + @pyenv exec tox -e template -- $(cookiecutter) + +.PHONY: clean +$(call help,make clean,"delete temporary files etc") +clean: + @rm -rf build dist .tox + @find . -path '*/__pycache__*' -delete + @find . -path '*.egg-info*' -delete + +.PHONY: python +python: + @bin/make_python + +-include cookiecutter_pypackage_test.mk diff --git a/README.md b/README.md new file mode 100644 index 0000000..c9de193 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ + + + + + + +# cookiecutter-pypackage-test + +A test package for our pypackage cookiecutter. + +For installation instructions see [INSTALL.md](https://github.com/hypothesis/cookiecutter-pypackage-test/blob/main/INSTALL.md). + +For how to set up a cookiecutter-pypackage-test development environment see +[HACKING.md](https://github.com/hypothesis/cookiecutter-pypackage-test/blob/main/HACKING.md). diff --git a/bin/make_python b/bin/make_python new file mode 100755 index 0000000..39a6326 --- /dev/null +++ b/bin/make_python @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Ensure that each version of Python in the .python_version file is installed +# in pyenv and that tox is installed within each version of Python. +set -euo pipefail + +if [ -n "${CI+x}" ]; then exit; fi + +pyenv_root=$(pyenv root) + +for python_version in 3.10.4 3.9.12 3.8.13; do + bin_dir=$pyenv_root/versions/$python_version/bin + if [ ! -f "$bin_dir"/tox ]; then + pyenv install --skip-existing "$python_version" + "$bin_dir"/pip install --disable-pip-version-check tox + pyenv rehash + fi +done diff --git a/bin/make_template b/bin/make_template new file mode 100755 index 0000000..74f5cda --- /dev/null +++ b/bin/make_template @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +import json +import os +import sys +from pathlib import Path +from tempfile import TemporaryDirectory + +from cookiecutter.main import cookiecutter + +with open(".cookiecutter/cookiecutter.json", "r", encoding="utf-8") as cookiecutter_json_file: + config = json.loads(cookiecutter_json_file.read()) + +extra_context = config.get("extra_context", {}) +extra_context["__ignore__"] = config.get("ignore", []) +extra_context["__target_dir__"] = Path(os.getcwd()) + +with TemporaryDirectory() as tmpdirname: + cookiecutter( + template=sys.argv[1] if len(sys.argv) >= 2 else config["template"], + directory=config["directory"], + extra_context=extra_context, + no_input=True, + overwrite_if_exists=True, + output_dir=tmpdirname, + ) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b9bd696 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,135 @@ +[build-system] +requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"] +build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] + +[tool.pytest.ini_options] +addopts = "-q" +filterwarnings = [ + "error", # Fail the tests if there are any warnings. + "ignore:^find_module\\(\\) is deprecated and slated for removal in Python 3.12; use find_spec\\(\\) instead$:DeprecationWarning:importlib", + "ignore:^FileFinder.find_loader\\(\\) is deprecated and slated for removal in Python 3.12; use find_spec\\(\\) instead$:DeprecationWarning:importlib", +] + +[tool.pydocstyle] +ignore = [ + # Missing docstrings. + "D100","D101","D102","D103","D104","D105","D106","D107", + + # "No blank lines allowed after function docstring" conflicts with the + # Black code formatter which insists on inserting blank lines after + # function docstrings. + "D202", + + # "1 blank line required before class docstring" conflicts with another + # pydocstyle rule D211 "No blank lines allowed before class docstring". + "D203", + + # "Multi-line docstring summary should start at the first line" + # and "Multi-line docstring summary should start at the second line". + # These two rules conflict with each other so you have to disable one of them. + # How about we disable them both? PEP 257 says either approach is okay: + # + # > The summary line may be on the same line as the opening quotes or on + # > the next line. + # > + # > https://peps.python.org/pep-0257/#multi-line-docstrings + "D212", + "D213", +] + +[tool.coverage.run] +branch = true +parallel = true +source = ["cookiecutter_pypackage_test", "tests/unit"] + +[tool.coverage.paths] +source = ["src", ".tox/*tests/lib/python*/site-packages"] + +[tool.coverage.report] +show_missing = true +precision = 2 +fail_under = 100.00 +skip_covered = true + +[tool.isort] +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 0 +use_parentheses = true +line_length = 88 +default_section = "THIRDPARTY" +known_first_party = ["cookiecutter_pypackage_test", "tests"] + +[tool.pylint.main] +jobs = 0 # Use one process for CPU. + +load-plugins = [ + "pylint.extensions.bad_builtin", + "pylint.extensions.check_elif", + "pylint.extensions.comparetozero", + "pylint.extensions.docparams", + "pylint.extensions.emptystring", + "pylint.extensions.mccabe", + "pylint.extensions.overlapping_exceptions", + "pylint.extensions.redefined_variable_type", +] + +# Fail if there are *any* messages from PyLint. +# The letters refer to PyLint's message categories, see +# https://pylint.pycqa.org/en/latest/messages/messages_introduction.html +fail-on = ["C", "E", "F", "I", "R", "W"] + +[tool.pylint.messages_control] +enable = [ + "bad-inline-option", + "deprecated-pragma", + "useless-suppression", + "use-symbolic-message-instead", +] +disable = [ + # Docstrings are encouraged but we don't want to enforce that everything + # must have a docstring. + "missing-docstring", + + # We don't always want to have to put a `:return:` in a docstring. + "missing-return-doc", + + # We don't always want to have to put an `:rtype:` in a docstring. + "missing-return-type-doc", + + # We don't want to have to document the type of every parameter with a + # `:type:` in the docstring. + "missing-type-doc", + + # We use isort to sort and group our imports, so we don't need PyLint to + # check them for us. + "ungrouped-imports", + + # We use Black to format our code automatically, so we don't need PyLint to + # check formatting for us. + "line-too-long", + + # We use isort to sort out imports so we don't need PyLint to check import + # ordering for us. + "wrong-import-order", + + "too-few-public-methods", + + # Issues to disable this for false positives, disabling it globally in the meantime https://github.com/PyCQA/pylint/issues/214 + "duplicate-code", +] + +good-names = [ + "i", "j", "k", "ex", "Run", "_", # PyLint's default good names. + "tm", "db", "ai", +] + +[tool.pylint.reports] +output-format = "colorized" +score = "no" + +[tool.hdev] +project_name = "cookiecutter-pypackage-test" +project_type = "package" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..c49e819 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,42 @@ +[metadata] +name = cookiecutter-pypackage-test +description = A test package for our pypackage cookiecutter. +long_description = file: README.md +long_description_content_type = text/markdown +url = https://github.com/hypothesis/cookiecutter-pypackage-test +project_urls = + Bug Tracker = https://github.com/hypothesis/cookiecutter-pypackage-test/issues + Changelog = https://github.com/hypothesis/cookiecutter-pypackage-test/releases +classifiers = + Programming Language :: Python :: 3 + License :: OSI Approved :: BSD License + Intended Audience :: Developers + +[options] +package_dir = + = src +packages = find: +python_requires = >=3.8 + +[options.packages.find] +where = src + +[options.entry_points] +console_scripts = + cookiecutter-pypackage-test = cookiecutter_pypackage_test.main:entry_point + +[pycodestyle] +ignore = + # Disable pycodestyle errors and warnings that we don't care about because + # Black formats our code for us. + E203, # Whitespace before ':', + E231, # Missing whitespace after ',', + E501, # Line too long, + W503, # Line break before binary operator, + + # "Comparison to None should be 'if cond is None:'. + # PyLint finds these so we don't need pycodestyle to. + E711, + + # Bare except. PyLint finds these for us so we don't need pycodestyle to. + E722, diff --git a/src/cookiecutter_pypackage_test/__init__.py b/src/cookiecutter_pypackage_test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/cookiecutter_pypackage_test/main.py b/src/cookiecutter_pypackage_test/main.py new file mode 100644 index 0000000..62058fd --- /dev/null +++ b/src/cookiecutter_pypackage_test/main.py @@ -0,0 +1,18 @@ +import sys +from argparse import ArgumentParser +from importlib.metadata import version + + +def hello_world(): + return "Hello, world!" + + +def entry_point(): # pragma: nocover + parser = ArgumentParser() + parser.add_argument("-v", "--version", action="store_true") + + args = parser.parse_args() + + if args.version: + print(version("cookiecutter-pypackage-test")) + sys.exit() diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/functional/cookiecutter_pypackage_test_test.py b/tests/functional/cookiecutter_pypackage_test_test.py new file mode 100644 index 0000000..b76ddae --- /dev/null +++ b/tests/functional/cookiecutter_pypackage_test_test.py @@ -0,0 +1,2 @@ +def test_it(): + assert True diff --git a/tests/pyproject.toml b/tests/pyproject.toml new file mode 100644 index 0000000..4b374da --- /dev/null +++ b/tests/pyproject.toml @@ -0,0 +1,92 @@ +[tool.pylint.main] +jobs = 0 # Use one process for CPU. + +load-plugins = [ + "pylint.extensions.bad_builtin", + "pylint.extensions.check_elif", + "pylint.extensions.comparetozero", + "pylint.extensions.docparams", + "pylint.extensions.emptystring", + "pylint.extensions.mccabe", + "pylint.extensions.overlapping_exceptions", + "pylint.extensions.redefined_variable_type", +] + +# Fail if there are *any* messages from PyLint. +# The letters refer to PyLint's message categories, see +# https://pylint.pycqa.org/en/latest/messages/messages_introduction.html +fail-on = ["C", "E", "F", "I", "R", "W"] + +[tool.pylint.messages_control] +ignore-paths = [ +] +enable = [ + "bad-inline-option", + "deprecated-pragma", + "useless-suppression", + "use-symbolic-message-instead", +] +disable = [ + # Docstrings are encouraged but we don't want to enforce that everything + # must have a docstring. + "missing-docstring", + + # We don't always want to have to put a `:return:` in a docstring. + "missing-return-doc", + + # We don't always want to have to put an `:rtype:` in a docstring. + "missing-return-type-doc", + + # We don't want to have to document the type of every parameter with a + # `:type:` in the docstring. + "missing-type-doc", + + # We use isort to sort and group our imports, so we don't need PyLint to + # check them for us. + "ungrouped-imports", + + # We use Black to format our code automatically, so we don't need PyLint to + # check formatting for us. + "line-too-long", + + # Because of how pytest fixtures work it's frequently necessary for + # parameters to redefine outer names. + "redefined-outer-name", + + # Lots of test methods don't use self, but we still want to group our tests + # into classes. + "too-few-public-methods", + "too-many-public-methods", + + "too-many-arguments", + + # not-callable is mis-firing on all pytest.mark.parametrize usages, so + # disable it for now. This can be re-enabled once a new pytest version + # including https://github.com/pytest-dev/pytest/pull/7565 has been + # released. + "not-callable", + + # Issues to disable this for false positives, disabling it globally in the meantime https://github.com/PyCQA/pylint/issues/214 + "duplicate-code", +] + +# Just disable PyLint's name style checking for the tests, because we +# frequently use lots of argument names that don't conform. +# For example we frequently create pytest fixtures that aren't named in +# snake_case, such as a fixture that returns a mock of the FooBar class would +# be named FooBar in CamelCase. +argument-naming-style = "any" +class-naming-style = "any" +function-naming-style = "any" +method-naming-style = "any" +variable-naming-style = "any" + +good-names = [ + "i", "j", "k", "ex", "Run", "_", # PyLint's default good names. + "pytestmark", + "os", # Name used for mocks of the standard os module. +] + +[tool.pylint.reports] +output-format = "colorized" +score = "no" diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/cookiecutter_pypackage_test/__init__.py b/tests/unit/cookiecutter_pypackage_test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/cookiecutter_pypackage_test/main_test.py b/tests/unit/cookiecutter_pypackage_test/main_test.py new file mode 100644 index 0000000..5e08daf --- /dev/null +++ b/tests/unit/cookiecutter_pypackage_test/main_test.py @@ -0,0 +1,6 @@ +from cookiecutter_pypackage_test.main import hello_world + + +class TestHelloWorld: + def test_it(self): + assert hello_world() == "Hello, world!" diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..2efc097 --- /dev/null +++ b/tox.ini @@ -0,0 +1,49 @@ +[tox] +envlist = tests +minversion = 3.25.0 +requires = + tox-envfile + tox-faster + tox-run-command + tox-recreate +isolated_build = true + +[testenv] +skip_install = + format,checkformatting,coverage,template: true +setenv = + PYTHONUNBUFFERED = 1 + OBJC_DISABLE_INITIALIZE_FORK_SAFETY = YES +passenv = + HOME + PYTEST_ADDOPTS +deps = + dev: ipython + format,checkformatting: black + format,checkformatting: isort + lint: toml + lint: pylint + lint: pydocstyle + lint: pycodestyle + lint,tests,coverage: coverage[toml] + lint,tests,functests: pytest + lint,tests,functests: factory-boy + lint,tests,functests: h-matchers + lint,template: cookiecutter +depends = + coverage: tests,py{39,38}-tests +commands = + dev: {posargs:ipython --classic --no-banner --no-confirm-exit} + format: black src tests bin + format: isort --atomic src tests bin + checkformatting: black --check src tests bin + checkformatting: isort --quiet --check-only src tests bin + lint: pylint src bin + lint: pylint --rcfile=tests/pyproject.toml tests + lint: pydocstyle src tests bin + lint: pycodestyle src tests bin + tests: coverage run -m pytest --failed-first --new-first --no-header --quiet {posargs:tests/unit/} + functests: pytest --failed-first --new-first --no-header --quiet {posargs:tests/functional/} + coverage: -coverage combine + coverage: coverage report + template: python3 bin/make_template {posargs}