├── .github ├── dependabot.yml └── workflows │ └── python-package.yml ├── .gitignore ├── .pre-commit-config.yaml ├── LICENSE ├── README.md ├── pyproject.toml ├── requirements.txt ├── src └── bulletproof_python │ ├── __init__.py │ └── main.py ├── tests ├── __init__.py └── test_main.py └── tox.ini /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "pip" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | - package-ecosystem: "github-actions" 13 | directory: ".github/workflows" 14 | schedule: 15 | interval: "daily" 16 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Lint, type-check and test 5 | 6 | on: 7 | push: 8 | branches: ["main"] 9 | paths-ignore: 10 | - "README.md" 11 | - "LICENSE" 12 | pull_request: 13 | branches: ["main"] 14 | paths-ignore: 15 | - "README.md" 16 | - "LICENSE" 17 | 18 | jobs: 19 | test: 20 | name: test ${{ matrix.py }} on ${{ matrix.os }} 21 | runs-on: ${{ matrix.os }} 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | py: 26 | - "3.13" 27 | - "3.12" 28 | - "3.11" 29 | - "3.10" 30 | os: 31 | - ubuntu-latest 32 | - windows-latest 33 | - macos-latest 34 | steps: 35 | - name: Setup python for tox 36 | uses: actions/setup-python@v5 37 | with: 38 | python-version: "3.12" 39 | - uses: actions/checkout@v4 40 | with: 41 | fetch-depth: 0 42 | - name: Install tox 43 | run: python -m pip install tox 44 | - name: Setup python for test ${{ matrix.py }} 45 | uses: actions/setup-python@v5 46 | with: 47 | python-version: ${{ matrix.py }} 48 | - name: Pick environment to run 49 | run: | 50 | import os; import platform; import sys; from pathlib import Path 51 | env = f'TOXENV=py{"" if platform.python_implementation() == "CPython" else "py"}3{sys.version_info.minor}' 52 | print(f"Picked: {env} for {sys.version} based of {sys.executable}") 53 | with Path(os.environ["GITHUB_ENV"]).open("ta") as file_handler: 54 | file_handler.write(env) 55 | shell: python 56 | - name: Setup test suite 57 | run: tox r -vv --notest 58 | - name: Run test suite 59 | run: tox r --skip-pkg-install 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .envrc 107 | .venv 108 | env/ 109 | venv/ 110 | ENV/ 111 | env.bak/ 112 | venv.bak/ 113 | 114 | # Spyder project settings 115 | .spyderproject 116 | .spyproject 117 | 118 | # Rope project settings 119 | .ropeproject 120 | 121 | # mkdocs documentation 122 | /site 123 | 124 | # mypy 125 | .mypy_cache/ 126 | .dmypy.json 127 | dmypy.json 128 | 129 | # Pyre type checker 130 | .pyre/ 131 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v5.0.0 4 | hooks: 5 | - id: trailing-whitespace 6 | - id: end-of-file-fixer 7 | - id: check-yaml 8 | - id: check-added-large-files 9 | - repo: https://github.com/psf/black 10 | rev: 24.10.0 11 | hooks: 12 | - id: black 13 | - repo: https://github.com/charliermarsh/ruff-pre-commit 14 | rev: v0.8.2 15 | hooks: 16 | - id: ruff 17 | - repo: https://github.com/pre-commit/mirrors-prettier 18 | rev: v4.0.0-alpha.8 19 | hooks: 20 | - id: prettier 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) James Williams 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bulletproof-python 2 | 3 | > [!IMPORTANT] 4 | > This template has been archived. I am starting my new projects with [uv](https://docs.astral.sh/uv/). 5 | 6 | A magic-free, understandable python project template using tox, ruff, pytest and 7 | pip-tools. This template requires you to manage your own virtual environment, 8 | but the configuration is explicit and easy to understand. 9 | 10 | This is a great starting point for internal tools, but it is just that. There 11 | are a great many best practices with respect to packaging and distribution that 12 | are not covered here, and for which I refer you to 13 | [Seth Michael Larson's secure-python-package-template](https://github.com/sethmlarson/secure-python-package-template). 14 | 15 | Moreover, since I created this template, ruff has become a 16 | [for-profit company](https://astral.sh) and has integrated code formatting that 17 | replaces black. They have also developed [uv](https://docs.astral.sh/uv/) which 18 | has earned a strong foothold in the python workflow space. Much of this template 19 | could be simplified using those cutting edge tools, however I likely will not 20 | update it to match as I feel it is a useful reference for those who may not wish 21 | to depend entirely on a vc-backed ecosystem. 22 | 23 | ## Background 24 | 25 | I owe a debt of gratitude to [Claudio Jolowicz](https://github.com/cjolowicz/) 26 | for introducing me to modern python tooling. His 27 | [Hypermodern Python](https://github.com/cjolowicz/cookiecutter-hypermodern-python) 28 | template and associated blog series are a great resource. 29 | 30 | The Hypermodern system is built around [nox](https://nox.thea.codes/en/stable/) 31 | and [poetry](https://python-poetry.org). 32 | 33 | - nox is similar in spirit to [tox](https://tox.wiki/en/latest/) but uses python 34 | scripts instead of configuration files. 35 | - It's very powerful, but since I'm not particularly clever, I find the 36 | hypermodern stack difficult to understand. 37 | 38 | I decided to throw together a simple template that uses tox and pip-tools. The 39 | template uses: 40 | 41 | - [pytest](https://docs.pytest.org/en/7.2.x/) and 42 | [coverage.py](https://coverage.readthedocs.io/en/latest/) for testing, 43 | - [black](https://black.readthedocs.io/en/stable/) for formatting, 44 | - [ruff](https://github.com/charliermarsh/ruff) for linting, 45 | - [pip-tools](https://github.com/jazzband/pip-tools) for managing dependencies, 46 | and 47 | - [tox](https://tox.wiki/en/latest/) for coordinating it all. 48 | 49 | It strives to be clean, simple and hard to break, albeit with a little extra 50 | elbow grease involved relative to more batteries-included tools like poetry. 51 | 52 | ## Quick-start 53 | 54 | 1. Install tox using pipx 55 | 1. Click 'Use this template' to create a new repository and clone it 56 | 1. Create and activate a virtual environment 57 | 1. Install the dependencies with 58 | `python -m pip install --upgrade pip setuptools wheel pip-tools` followed by 59 | `pip-sync` 60 | 1. Install the first-party package in editable mode: `pip install -e .` 61 | 1. Install the pre-commit hooks: `pre-commit install` 62 | 1. Run the test suite with tox: `tox` 63 | 1. Replace the name bulletproof_python/bulletproof-python with your project name 64 | 1. Update project metadata in pyproject.toml and README.md 65 | 1. Write some code and lint it: `tox -e fix`. 66 | 67 | ## Detailed Usage 68 | 69 | ### 1. Install tox using pipx 70 | 71 | Tox is a tool for automating testing and development tasks in isolated 72 | environments. You can use pipx, a package runner for Python applications, to 73 | install tox. The command is simply: 74 | 75 | ```bash 76 | pipx install tox 77 | ``` 78 | 79 | ### 2. Create a New Repository and Clone it 80 | 81 | The 'Use this template' button on GitHub allows you to create a new repository 82 | from a template repository like this one. After creating your new repository, 83 | clone it onto your local machine using: 84 | 85 | ```bash 86 | git clone https://github.com/your_username/your_repository.git 87 | ``` 88 | 89 | ### 3. Create and Activate a Virtual Environment 90 | 91 | Virtual environments in Python isolate project dependencies, ensuring different 92 | projects don't interfere with each other. To create and activate a new virtual 93 | environment: 94 | 95 | ```bash 96 | python -m venv .venv 97 | source .venv/bin/activate 98 | ``` 99 | 100 | ### 4. Install the Dependencies 101 | 102 | The requirements.txt file lists the Python dependencies for your project. Using 103 | pip-tools, we can pin the exact version of each dependency to ensure 104 | reproducible builds. 105 | 106 | Install the core python dependencies then install the project dependencies using 107 | pip-tools: 108 | 109 | ```bash 110 | python -m pip install --upgrade pip setuptools wheel pip-tools 111 | pip-sync 112 | ``` 113 | 114 | You can optionally use pip-tools to update all dependencies before running 115 | `pip-sync`. This isn't strictly necessary as I keep this repository more-or-less 116 | up to date, and dependabot will submit pull requests for out of date packages 117 | automatically. 118 | 119 | ```bash 120 | pip-compile --all-extras --resolver=backtracking --upgrade pyproject.toml 121 | ``` 122 | 123 | Note: You can always reference the command to pin dependencies in a comment at 124 | the top of requirements.txt. 125 | 126 | ### 5. Install the First-Party Package in Editable Mode 127 | 128 | The -e option allows pip to install the package in editable mode. This means 129 | changes to the source code will immediately affect the package without needing 130 | to reinstall it: 131 | 132 | ```bash 133 | python -m pip install -e . 134 | ``` 135 | 136 | ### 6. Install the Pre-commit Hooks 137 | 138 | Pre-commit is a framework for managing git pre-commit hooks. These checks are 139 | run before your changes are committed to ensure code quality. To install 140 | pre-commit hooks: 141 | 142 | ```bash 143 | pre-commit install 144 | ``` 145 | 146 | You can also update the hook versions: 147 | 148 | ```bash 149 | pre-commit autoupdate 150 | ``` 151 | 152 | ### 7. Run the Test Suite with tox 153 | 154 | Now you can run the project's test suite using the previously installed tox 155 | tool: 156 | 157 | ```bash 158 | tox 159 | ``` 160 | 161 | By default, this will create environments for py310, py311, py312 and py313. 162 | This assumes all versions of python are available on your system. Take a look at 163 | pyenv to make this easy. 164 | 165 | You can add additional python versions to test against by modifying `tox.ini` 166 | and the corresponding github-action workflow. 167 | 168 | ### 8. Replace the Project Name 169 | 170 | Now replace the placeholder project name bulletproof_python/bulletproof-python 171 | with the name of your actual project throughout the project files. 172 | 173 | ### 9. Update Project Metadata 174 | 175 | Update the project metadata in both pyproject.toml and README.md to match your 176 | project's details. 177 | 178 | ### 10. Write Code and Lint it 179 | 180 | Now write your code! After that, you can run linting checks and auto-fix issues 181 | using tox: 182 | 183 | ```bash 184 | tox -e fix 185 | ``` 186 | 187 | Take a look at `tox.ini` for a reference on using the various tools directly 188 | from the command line. During development, you don't always need to execute 189 | through tox's isolated environments. 190 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "bulletproof-python" 7 | version = "0.1.0" 8 | description = 'Update me...' 9 | readme = "README.md" 10 | requires-python = ">=3.10" 11 | keywords = [] 12 | authors = [{ name = "James Williams", email = "james@kananlabs.org" }] 13 | classifiers = [ 14 | "Development Status :: 1 - Planning", 15 | "Programming Language :: Python", 16 | "Programming Language :: Python :: 3.10", 17 | "Programming Language :: Python :: 3.11", 18 | "Programming Language :: Python :: 3.12", 19 | "Programming Language :: Python :: 3.13", 20 | "Programming Language :: Python :: Implementation :: CPython", 21 | ] 22 | dependencies = [] 23 | 24 | [project.optional-dependencies] 25 | dev = ["black", "coverage", "mypy", "pip-tools", "pre-commit", "pytest", "ruff"] 26 | 27 | [project.scripts] 28 | bulletproof-python = "bulletproof_python.main:main" 29 | 30 | [project.urls] 31 | Documentation = "https://github.com/jamwil/bulletproof-python#readme" 32 | Issues = "https://github.com/jamwil/bulletproof-python/issues" 33 | Source = "https://github.com/jamwil/bulletproof-python" 34 | 35 | [tool.setuptools] 36 | package-dir = { "" = "src" } 37 | 38 | [tool.setuptools.packages.find] 39 | where = ["src"] 40 | 41 | [tool.black] 42 | preview = true 43 | 44 | [tool.coverage.run] 45 | branch = true 46 | source = ["src", "tests"] 47 | 48 | [tool.coverage.paths] 49 | source = ["src", ".tox/*/lib/python*/site-packages"] 50 | 51 | [tool.coverage.report] 52 | exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:"] 53 | fail_under = 100 54 | show_missing = true 55 | 56 | [tool.mypy] 57 | python_version = "3.13" 58 | warn_unused_configs = true 59 | warn_unused_ignores = true 60 | warn_redundant_casts = true 61 | warn_return_any = true 62 | warn_unreachable = true 63 | strict = true 64 | overrides = [{ module = [], ignore_missing_imports = true }] 65 | 66 | [tool.ruff] 67 | target-version = "py312" 68 | src = ["src", "tests"] 69 | preview = true 70 | 71 | [tool.ruff.lint] 72 | select = [ 73 | "E", # pycodestyle 74 | "F", # pyflakes 75 | "W", # pycodestyle 76 | "C90", # mccabe 77 | "I", # isort 78 | "D", # pydocstyle 79 | "UP", # pyupgrade 80 | "N", # pep8-naming 81 | "YTT", # flake8-2020 82 | "ANN", # flake8-annotations 83 | "ASYNC", # flake8-async 84 | "S", # flake8-bandit 85 | "BLE", # flake8-blind-except 86 | "FBT", # flake8-boolean-trap 87 | "B", # flake8-bugbear 88 | "A", # flake8-builtins 89 | "COM", # flake8-commas 90 | "CPY", # copyright-related rules 91 | "C4", # flake8-comprehensions 92 | "DTZ", # flake8-datetimez 93 | "T10", # flake8-debugger 94 | "DJ", # flake8-django 95 | "EM", # flake8-errmsg 96 | "EXE", # flake8-executable 97 | "FA", # flake8-future-annotations 98 | "ISC", # flake8-implicit-str-concat 99 | "ICN", # flake8-import-conventions 100 | "G", # flake8-logging-format 101 | "INP", # flake8-no-pep420 102 | "PIE", # flake8-pie 103 | "T20", # flake8-print 104 | "PYI", # flake8-pyi 105 | "PT", # flake8-pytest-style 106 | "Q", # flake8-quotes 107 | "RSE", # flake8-raise 108 | "RET", # flake8-return 109 | "SLF", # flake8-self 110 | "SLOT", # flake8-slots 111 | "SIM", # flake8-simplify 112 | "TID", # flake8-tidy-imports 113 | "TCH", # flake8-type-checking 114 | "INT", # flake8-type-checking 115 | "ARG", # flake8-unused-arguments 116 | "PTH", # flake8-use-pathlib 117 | "TD", # flake8-todos 118 | "FIX", # flake8-fixme 119 | "ERA", # eradicate 120 | "PD", # pandas-vet 121 | "PGH", # pygrep-hooks 122 | "PL", # pylint 123 | "TRY", # tryceratops 124 | "FLY", # flynt 125 | "NPY", # NumPy-specific rules 126 | "AIR", # Airflow 127 | "PERF", # Perflint 128 | "FURB", # refurb 129 | "RUF", # Ruff-specific rules 130 | ] 131 | ignore = ["ANN101", "CPY001"] # annotations for self 132 | 133 | [tool.ruff.lint.per-file-ignores] 134 | "tests/*.py" = [ 135 | "PLR2004", # magic value comparison 136 | "S101", # use of assert detected 137 | "TCH002", # third party import (for pytest) 138 | ] 139 | 140 | [tool.ruff.lint.pydocstyle] 141 | convention = "pep257" 142 | 143 | [tool.ruff.lint.isort] 144 | known-first-party = ["bulletproof_python"] 145 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with Python 3.11 3 | # by the following command: 4 | # 5 | # pip-compile --all-extras pyproject.toml 6 | # 7 | black==24.10.0 8 | # via bulletproof-python (pyproject.toml) 9 | build==1.2.2.post1 10 | # via pip-tools 11 | cfgv==3.4.0 12 | # via pre-commit 13 | click==8.1.7 14 | # via 15 | # black 16 | # pip-tools 17 | coverage==7.6.9 18 | # via bulletproof-python (pyproject.toml) 19 | distlib==0.3.9 20 | # via virtualenv 21 | filelock==3.16.1 22 | # via virtualenv 23 | identify==2.6.3 24 | # via pre-commit 25 | iniconfig==2.0.0 26 | # via pytest 27 | mypy==1.15.0 28 | # via bulletproof-python (pyproject.toml) 29 | mypy-extensions==1.0.0 30 | # via 31 | # black 32 | # mypy 33 | nodeenv==1.9.1 34 | # via pre-commit 35 | packaging==24.2 36 | # via 37 | # black 38 | # build 39 | # pytest 40 | pathspec==0.12.1 41 | # via black 42 | pip-tools==7.4.1 43 | # via bulletproof-python (pyproject.toml) 44 | platformdirs==4.3.6 45 | # via 46 | # black 47 | # virtualenv 48 | pluggy==1.5.0 49 | # via pytest 50 | pre-commit==4.0.1 51 | # via bulletproof-python (pyproject.toml) 52 | pyproject-hooks==1.2.0 53 | # via 54 | # build 55 | # pip-tools 56 | pytest==8.3.4 57 | # via bulletproof-python (pyproject.toml) 58 | pyyaml==6.0.2 59 | # via pre-commit 60 | ruff==0.8.2 61 | # via bulletproof-python (pyproject.toml) 62 | typing-extensions==4.12.2 63 | # via mypy 64 | virtualenv==20.28.0 65 | # via pre-commit 66 | wheel==0.45.1 67 | # via pip-tools 68 | 69 | # The following packages are considered to be unsafe in a requirements file: 70 | # pip 71 | # setuptools 72 | -------------------------------------------------------------------------------- /src/bulletproof_python/__init__.py: -------------------------------------------------------------------------------- 1 | """A magic-free, understandable python project template.""" 2 | -------------------------------------------------------------------------------- /src/bulletproof_python/main.py: -------------------------------------------------------------------------------- 1 | """Entry point for the package.""" 2 | 3 | import logging 4 | 5 | 6 | def main() -> None: 7 | """Entry point for the application script.""" 8 | logging.warning("Hello World") 9 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Unit and integration tests.""" 2 | -------------------------------------------------------------------------------- /tests/test_main.py: -------------------------------------------------------------------------------- 1 | """Unit tests for the entry point.""" 2 | 3 | import logging 4 | 5 | import pytest 6 | 7 | from bulletproof_python import main 8 | 9 | 10 | def test_main(caplog: pytest.LogCaptureFixture) -> None: 11 | """Test the entry point.""" 12 | main.main() 13 | assert caplog.record_tuples == [ 14 | ("root", logging.WARNING, "Hello World"), 15 | ] 16 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | isolated_build = True 3 | min_version = 4.0.0 4 | envlist = 5 | py310 6 | py311 7 | py312 8 | py313 9 | lint 10 | type 11 | 12 | [testenv] 13 | description = Run all tests/checks 14 | package = wheel 15 | wheel_build_env = .pkg # Only works with universal wheels 16 | deps = 17 | -r requirements.txt 18 | setenv = 19 | COVERAGE_FILE = {toxworkdir}{/}.coverage.{envname} 20 | PYTHONWARNINGS = ignore 21 | commands = 22 | coverage run \ 23 | --source={envsitepackagesdir}{/}bulletproof_python,{toxinidir}{/}tests \ 24 | -m pytest {posargs} 25 | coverage report 26 | 27 | [testenv:lint] 28 | description = Run linters 29 | skip_install = True 30 | deps = 31 | black 32 | ruff 33 | commands = 34 | black --check src tests 35 | ruff check src tests 36 | 37 | [testenv:type] 38 | description = Run type checks 39 | skip_install = True 40 | commands = 41 | mypy --config-file=pyproject.toml src tests 42 | 43 | [testenv:fix] 44 | description = Run checks that may modify files 45 | skip_install = True 46 | deps = 47 | black 48 | ruff 49 | pre-commit 50 | commands = 51 | black src tests 52 | ruff --fix src tests 53 | pre-commit run --all-files 54 | --------------------------------------------------------------------------------