From cd1a5768c078888380a5c793f450227b0e48cd5b Mon Sep 17 00:00:00 2001 From: Marcos Prieto Date: Fri, 11 Nov 2022 11:17:38 +0100 Subject: [PATCH] Apply the library cookiecutter --- .cookiecutter/cookiecutter.json | 24 +++++ .github/dependabot.yml | 9 ++ .github/workflows/ci.yml | 76 ++++++++++++++++ .github/workflows/pypi.yml | 8 ++ .gitignore | 12 +++ .python-version | 4 + LICENSE | 25 ++++++ Makefile | 98 ++++++++++++++++++++ README.md | 110 +++++++++++++++++++++++ bin/make_python | 18 ++++ bin/make_template | 32 +++++++ pyproject.toml | 138 +++++++++++++++++++++++++++++ setup.cfg | 42 +++++++++ src/data_tasks/__init__.py | 0 src/data_tasks/core.py | 2 + tests/__init__.py | 0 tests/functional/__init__.py | 0 tests/functional/sanity_test.py | 2 + tests/pyproject.toml | 92 +++++++++++++++++++ tests/unit/__init__.py | 0 tests/unit/data_tasks/__init__.py | 0 tests/unit/data_tasks/core_test.py | 6 ++ tox.ini | 52 +++++++++++ 23 files changed, 750 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 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/data_tasks/__init__.py create mode 100644 src/data_tasks/core.py create mode 100644 tests/__init__.py create mode 100644 tests/functional/__init__.py create mode 100644 tests/functional/sanity_test.py create mode 100644 tests/pyproject.toml create mode 100644 tests/unit/__init__.py create mode 100644 tests/unit/data_tasks/__init__.py create mode 100644 tests/unit/data_tasks/core_test.py create mode 100644 tox.ini diff --git a/.cookiecutter/cookiecutter.json b/.cookiecutter/cookiecutter.json new file mode 100644 index 0000000..27ea8a8 --- /dev/null +++ b/.cookiecutter/cookiecutter.json @@ -0,0 +1,24 @@ +{ + "template": "https://github.com/hypothesis/cookiecutters", + "checkout": null, + "directory": "pypackage", + "ignore": [], + "extra_context": { + "name": "data-tasks", + "package_name": "data_tasks", + "slug": "data-tasks", + "short_description": "Small library to run data related scripts", + "python_versions": "3.10.6, 3.9.13, 3.8.13, 3.7.13", + "github_owner": "hypothesis", + "copyright_holder": "Hypothesis", + "visibility": "public", + "console_script": "no", + "devdata": "no", + "services": "no", + "dependabot_pip_interval": "monthly", + "__entry_point": "data-tasks", + "__github_url": "https://github.com/hypothesis/data-tasks", + "__pypi_url": "https://pypi.org/project/data-tasks", + "__hdev_project_type": "library" + } +} \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1c82127 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: +- package-ecosystem: "pip" + directory: "/" + schedule: + interval: "monthly" + 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..d8d4286 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +name: CI +on: + push: + workflow_dispatch: + workflow_call: + 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', '3.7'] + 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', '3.7'] + 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..f1e2c19 --- /dev/null +++ b/.python-version @@ -0,0 +1,4 @@ +3.10.6 +3.9.13 +3.8.13 +3.7.13 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..b3867f0 --- /dev/null +++ b/Makefile @@ -0,0 +1,98 @@ +comma := , + +.PHONY: help +help = help::; @echo $$$$(tput bold)$(strip $(1)):$$$$(tput sgr0) $(strip $(2)) +$(call help,make help,print this help message) + +.PHONY: services + +.PHONY: devdata + +.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") +test: python + @pyenv exec tox -qe tests + +.PHONY: test-py39 +$(call help,make test-py39,"run the unit tests in Python 3.9") +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") +test-py38: python + @pyenv exec tox -qe py38-tests + +.PHONY: test-py37 +$(call help,make test-py37,"run the unit tests in Python 3.7") +test-py37: python + @pyenv exec tox -qe py37-tests + +.PHONY: coverage +$(call help,make coverage,"run the tests and print the coverage report") +coverage: python + @pyenv exec tox --parallel -qe 'tests,py{39,38,37}-tests,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: functests-py37 +$(call help,make functests-py37,"run the functional tests in Python 3.7") +functests-py37: python + @pyenv exec tox -qe py37-functests + +.PHONY: sure +$(call help,make sure,"make sure that the formatting$(comma) linting and tests all pass") +sure: python +sure: + @pyenv exec tox --parallel -qe 'checkformatting,lint,tests,py{39,38,37}-tests,coverage,functests,py{39,38,37}-functests' + +.PHONY: template +$(call help,make template,"update from the latest cookiecutter template") +template: python + @pyenv exec tox -e template -- $$(if [ -n "$${template+x}" ]; then echo "--template $$template"; fi) $$(if [ -n "$${checkout+x}" ]; then echo "--checkout $$checkout"; fi) $$(if [ -n "$${directory+x}" ]; then echo "--directory $$directory"; fi) + +.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 data_tasks.mk diff --git a/README.md b/README.md new file mode 100644 index 0000000..b785dab --- /dev/null +++ b/README.md @@ -0,0 +1,110 @@ + + + + + + + +# data-tasks + +Small library to run data related scripts + +## Setting up Your data-tasks 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/data-tasks.git +cd data-tasks +make help +``` + +## Releasing a New Version of the Project + +1. First, to get PyPI publishing working you need to go to: + + and add data-tasks to the `PYPI_TOKEN` secret's selected + repositories. + +2. Now that the data-tasks 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/bin/make_python b/bin/make_python new file mode 100755 index 0000000..eb1cc07 --- /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.6 3.9.13 3.8.13 3.7.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..3eb117a --- /dev/null +++ b/bin/make_template @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +import argparse +import json +import os +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()) + +parser = argparse.ArgumentParser(description="Update the project from the cookiecutter template") +parser.add_argument("--template", help="the cookiecutter template to use (default: what's in cookiecutter.json)") +parser.add_argument("--checkout", help="the branch, tag or commit of the cookiecutter template to use (default: what's in cookiecutter.json)") +parser.add_argument("--directory", help="the directory within the cookiecutter repo to use to use (default: what's in cookiecutter.json)") +args = parser.parse_args() + +with TemporaryDirectory() as tmpdirname: + cookiecutter( + template=args.template or config.get("template"), + checkout=args.checkout or config.get("checkout"), + directory=args.directory or config.get("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..e5c468e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,138 @@ +[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 = ["data_tasks", "tests/unit"] +omit = [ + "*/data_tasks/__main__.py", +] + +[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 = ["data_tasks", "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 = "data-tasks" +project_type = "library" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..41775b7 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,42 @@ +[metadata] +name = data-tasks +description = Small library to run data related scripts +long_description = file: README.md +long_description_content_type = text/markdown +url = https://github.com/hypothesis/data-tasks +project_urls = + Bug Tracker = https://github.com/hypothesis/data-tasks/issues + Changelog = https://github.com/hypothesis/data-tasks/releases +classifiers = + Programming Language :: Python :: 3 + License :: OSI Approved :: BSD License + Intended Audience :: Developers + +[options] +package_dir = + = src +packages = find: +python_requires = >=3.7 +install_requires = + importlib_metadata;python_version<"3.8." + +[options.packages.find] +where = src + +[options.entry_points] + +[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/data_tasks/__init__.py b/src/data_tasks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/data_tasks/core.py b/src/data_tasks/core.py new file mode 100644 index 0000000..d1b7a0d --- /dev/null +++ b/src/data_tasks/core.py @@ -0,0 +1,2 @@ +def hello_world(): + return "Hello, world!" 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/sanity_test.py b/tests/functional/sanity_test.py new file mode 100644 index 0000000..b76ddae --- /dev/null +++ b/tests/functional/sanity_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/data_tasks/__init__.py b/tests/unit/data_tasks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/data_tasks/core_test.py b/tests/unit/data_tasks/core_test.py new file mode 100644 index 0000000..4c4bae4 --- /dev/null +++ b/tests/unit/data_tasks/core_test.py @@ -0,0 +1,6 @@ +from data_tasks.core 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..dde960f --- /dev/null +++ b/tox.ini @@ -0,0 +1,52 @@ +[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 + dev: DEBUG + dev: SENTRY_DSN +deps = + dev: ipython + format,checkformatting: black + format,checkformatting: isort + lint: toml + lint: pylint + lint: pydocstyle + lint: pycodestyle + lint,tests: pytest-mock + 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,37}-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}