├── .bumpversion.cfg ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ ├── pre-commit-autoupdate.yml │ ├── release.yml │ └── tests.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yml ├── .safety-policy.yml ├── CHANGELOG.md ├── CITATION.cff ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.rst ├── README.md ├── SECURITY.md ├── assets └── logo.png ├── cookiecutter.json ├── docs ├── _static │ └── custom.css ├── changelog.md ├── conf.py ├── console_script_setup.rst ├── index.rst ├── invoke.rst ├── license.rst ├── prompts.rst ├── readme.md ├── requirements.txt └── tutorial.rst ├── hooks ├── post_gen_project.py └── pre_gen_project.py ├── noxfile.py ├── poetry.lock ├── pyproject.toml ├── tasks.py ├── tests └── test_bake_project.py └── {{cookiecutter.project_name}} ├── .bumpversion.cfg ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ ├── pre-commit-autoupdate.yml │ ├── release.yml │ └── tests.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yml ├── .safety-policy.yml ├── CHANGELOG.md ├── CITATION.cff ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.rst ├── README.md ├── SECURITY.md ├── codecov.yml ├── docs ├── _static │ └── custom.css ├── conf.py ├── index.rst ├── installation.rst ├── requirements.txt └── usage.rst ├── noxfile.py ├── pyproject.toml ├── src └── {{cookiecutter.project_slug}} │ ├── __init__.py │ ├── cli.py │ ├── py.typed │ └── {{cookiecutter.project_slug}}.py ├── tasks.py └── tests ├── __init__.py ├── test_cli.py └── test_{{cookiecutter.project_slug}}.py /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | commit = True 3 | tag = False 4 | current_version = 3.1.0 5 | 6 | [bumpversion:file:pyproject.toml] 7 | search = version = "{current_version}" 8 | replace = version = "{new_version}" 9 | 10 | [bumpversion:file(version):docs/conf.py] 11 | search = version = "{current_version}" 12 | replace = version = "{new_version}" 13 | 14 | [bumpversion:file(release):docs/conf.py] 15 | search = release = "{current_version}" 16 | replace = release = "{new_version}" 17 | 18 | [bumpversion:file(title):CHANGELOG.md] 19 | search = {#}{#} [Unreleased] 20 | replace = {#}{#} [Unreleased] 21 | 22 | {#}{#} [{new_version}] - {now:%Y-%m-%d} 23 | 24 | [bumpversion:file(links):CHANGELOG.md] 25 | search = [Unreleased]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v{current_version}...develop 26 | replace = [Unreleased]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v{new_version}...develop 27 | [{new_version}]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v{current_version}...v{new_version} 28 | 29 | [bumpversion:file:README.md] 30 | search = cookiecutter gh:fedejaure/cookiecutter-modern-pypackage --checkout v{current_version} 31 | replace = cookiecutter gh:fedejaure/cookiecutter-modern-pypackage --checkout v{new_version} 32 | 33 | [bumpversion:file:docs/tutorial.rst] 34 | search = $ cookiecutter gh:fedejaure/cookiecutter-modern-pypackage --checkout v{current_version} 35 | replace = $ cookiecutter gh:fedejaure/cookiecutter-modern-pypackage --checkout v{new_version} 36 | 37 | [bumpversion:file(version):CITATION.cff] 38 | search = version: {current_version} 39 | replace = version: {new_version} 40 | 41 | [bumpversion:file(tag):CITATION.cff] 42 | search = https://github.com/fedejaure/cookiecutter-modern-pypackage/releases/tag/v{current_version} 43 | replace = https://github.com/fedejaure/cookiecutter-modern-pypackage/releases/tag/v{new_version} 44 | 45 | [bumpversion:file(description):CITATION.cff] 46 | search = description: The Software Heritage link for version {current_version}. 47 | replace = description: The Software Heritage link for version {new_version}. 48 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This is a comment. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # More details are here: https://help.github.com/articles/about-codeowners/ 5 | 6 | # These owners will be the default owners for everything in 7 | # the repo. Unless a later match takes precedence, 8 | # @fedejaure will be requested for review when someone opens 9 | # a pull request. 10 | * @fedejaure 11 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This is a comment. 3 | # You can configure the sponsor button in your repository to include 4 | # sponsored developers in GitHub Sponsors, external funding platforms, 5 | # or a custom funding URL. 6 | 7 | # More details are here: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository 8 | github: fedejaure 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug report 3 | about: Create a report to help us improve 4 | labels: bug 5 | assignees: '' 6 | 7 | --- 8 | 9 | ## Expected Behavior 10 | 11 | 12 | ## Actual Behavior 13 | 14 | 15 | ## Steps to Reproduce the Problem 16 | 17 | 1. 18 | 1. 19 | 1. 20 | 21 | ## Specifications 22 | 23 | - Version: 24 | - Platform: 25 | - Subsystem: 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: [] 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature request 3 | about: Suggest an idea for this project 4 | labels: enhancement 5 | assignees: '' 6 | 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # 2 | 3 | ## Proposed Changes 4 | 5 | - 6 | - 7 | - 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | time: "08:00" 8 | timezone: "Europe/Amsterdam" 9 | labels: 10 | - "dependencies" 11 | - "github_actions" 12 | open-pull-requests-limit: 99 13 | target-branch: "develop" 14 | - package-ecosystem: pip 15 | directory: "/docs" 16 | schedule: 17 | interval: "monthly" 18 | time: "08:00" 19 | timezone: "Europe/Amsterdam" 20 | labels: 21 | - "dependencies" 22 | - "python" 23 | open-pull-requests-limit: 99 24 | target-branch: "develop" 25 | - package-ecosystem: pip 26 | directory: "/" 27 | schedule: 28 | interval: "monthly" 29 | time: "08:00" 30 | timezone: "Europe/Amsterdam" 31 | labels: 32 | - "dependencies" 33 | - "python" 34 | open-pull-requests-limit: 99 35 | target-branch: "develop" 36 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - develop 8 | pull_request: 9 | branches: 10 | - develop 11 | schedule: 12 | - cron: '0 6 1 * *' 13 | 14 | jobs: 15 | analyze: 16 | name: Analyze 17 | runs-on: ubuntu-latest 18 | permissions: 19 | actions: read 20 | contents: read 21 | security-events: write 22 | 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | language: [ 'python' ] 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v4.2.2 31 | 32 | # Initializes the CodeQL tools for scanning. 33 | - name: Initialize CodeQL 34 | uses: github/codeql-action/init@v3 35 | with: 36 | languages: ${{ matrix.language }} 37 | 38 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 39 | # If this step fails, then you should remove it and run the build manually (see below) 40 | - name: Autobuild 41 | uses: github/codeql-action/autobuild@v3 42 | 43 | - name: Perform CodeQL Analysis 44 | uses: github/codeql-action/analyze@v3 45 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit-autoupdate.yml: -------------------------------------------------------------------------------- 1 | name: "Pre-commit autoupdate" 2 | 3 | on: 4 | schedule: 5 | - cron: '0 6 1 * *' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | autoupdate: 10 | name: autoupdate 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4.2.2 14 | with: 15 | ref: develop 16 | 17 | - name: Set up Python 3.9 18 | uses: actions/setup-python@v5.3.0 19 | with: 20 | python-version: 3.9 21 | 22 | - name: Install system deps 23 | shell: bash 24 | run: | 25 | pip install poetry 26 | poetry config virtualenvs.in-project true 27 | poetry install --no-root --only dev --only linters --sync 28 | 29 | - name: Run autoupdate 30 | run: poetry run pre-commit autoupdate 31 | continue-on-error: true 32 | 33 | - name: Run pre-commit 34 | run: poetry run pre-commit run --all-files 35 | 36 | - uses: peter-evans/create-pull-request@v7.0.5 37 | with: 38 | token: ${{ secrets.GITHUB_TOKEN }} 39 | branch: chore-update-pre-commit-hooks 40 | title: Update pre-commit hooks 41 | commit-message: "Update pre-commit hooks" 42 | body: | 43 | # Update pre-commit hooks 44 | 45 | - Update pre-commit hooks to the latest version. 46 | delete-branch: true 47 | labels: | 48 | dependencies 49 | python 50 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | permissions: 9 | id-token: write 10 | contents: write 11 | 12 | jobs: 13 | github_release: 14 | name: Create Github Release 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4.2.2 18 | 19 | - name: Get version from tag 20 | id: tag_name 21 | shell: bash 22 | run: | 23 | echo ::set-output name=current_version::${GITHUB_REF#refs/tags/v} 24 | 25 | - name: Get Changelog Entry 26 | id: changelog_reader 27 | uses: mindsers/changelog-reader-action@v2.2.3 28 | with: 29 | version: ${{ steps.tag_name.outputs.current_version }} 30 | path: ./CHANGELOG.md 31 | 32 | - name: Create Release 33 | id: create_release 34 | uses: actions/create-release@v1.1.4 35 | env: 36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | with: 38 | tag_name: ${{ github.ref }} 39 | release_name: Release ${{ github.ref }} 40 | body: ${{ steps.changelog_reader.outputs.changes }} 41 | draft: false 42 | prerelease: false 43 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - develop 8 | pull_request: 9 | branches: 10 | - develop 11 | 12 | jobs: 13 | linting: 14 | name: Linting 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4.2.2 18 | 19 | - name: Set up Python 3.9 20 | uses: actions/setup-python@v5.3.0 21 | with: 22 | python-version: 3.9 23 | 24 | - name: Install system deps 25 | shell: bash 26 | run: | 27 | pip install poetry 28 | poetry config virtualenvs.in-project true 29 | poetry install --no-root --only dev --only linters --sync 30 | 31 | - name: Linting 32 | shell: bash 33 | run: poetry run pre-commit run --all-files 34 | 35 | tests: 36 | needs: linting 37 | name: ${{ matrix.os }} / ${{ matrix.python-version }} 38 | runs-on: ${{ matrix.os }}-latest 39 | strategy: 40 | matrix: 41 | os: [Ubuntu, MacOS] 42 | python-version: ['3.9', '3.10', '3.11', '3.12'] 43 | fail-fast: true 44 | steps: 45 | - uses: actions/checkout@v4.2.2 46 | 47 | - name: Set up Python ${{ matrix.python-version }} 48 | uses: actions/setup-python@v5.3.0 49 | with: 50 | python-version: ${{ matrix.python-version }} 51 | 52 | - name: Install system deps 53 | shell: bash 54 | run: | 55 | pip install nox-poetry 56 | pip install poetry 57 | poetry config virtualenvs.in-project true 58 | 59 | - name: Run mypy with nox 60 | shell: bash 61 | run: nox --force-color -s mypy-${{ matrix.python-version }} 62 | 63 | - name: Run tests with nox 64 | shell: bash 65 | run: nox --force-color -s tests-${{ matrix.python-version }} 66 | 67 | - name: Run security check 68 | if: matrix.python-version == '3.12' 69 | shell: bash 70 | run: nox --force-color -s security 71 | -------------------------------------------------------------------------------- /.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 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pytype type checker 129 | .pytype/ 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # Cookiecutter 135 | modern-python-boilerplate/ 136 | 137 | # Code editors 138 | .vscode 139 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | default_install_hook_types: 3 | - pre-commit 4 | - post-checkout 5 | - post-merge 6 | repos: 7 | - repo: https://github.com/pre-commit/pre-commit-hooks 8 | rev: v5.0.0 9 | hooks: 10 | - id: check-toml 11 | exclude: "^{{cookiecutter.project_name}}/" 12 | - id: check-json 13 | exclude: "^{{cookiecutter.project_name}}/" 14 | - id: check-yaml 15 | exclude: "^{{cookiecutter.project_name}}/" 16 | - id: debug-statements 17 | exclude: "^{{cookiecutter.project_name}}/" 18 | - id: check-merge-conflict 19 | - id: pretty-format-json 20 | args: [--autofix, '--no-sort-keys'] 21 | exclude: "^{{cookiecutter.project_name}}/" 22 | - id: end-of-file-fixer 23 | exclude: "^{{cookiecutter.project_name}}/" 24 | - id: trailing-whitespace 25 | exclude: | 26 | (?x)^( 27 | .bumpversion.cfg| 28 | {{cookiecutter.project_name}}/ 29 | ) 30 | - repo: https://github.com/timothycrosley/isort 31 | rev: 5.13.2 32 | hooks: 33 | - id: isort 34 | exclude: "^{{cookiecutter.project_name}}/" 35 | - repo: https://github.com/psf/black 36 | rev: 24.10.0 37 | hooks: 38 | - id: black 39 | exclude: "^{{cookiecutter.project_name}}/" 40 | - repo: https://github.com/astral-sh/ruff-pre-commit 41 | rev: v0.7.4 42 | hooks: 43 | - id: ruff 44 | args: [ --fix ] 45 | exclude: "^{{cookiecutter.project_name}}/" 46 | - repo: https://github.com/citation-file-format/cffconvert 47 | rev: 5295f87c0e261da61a7b919fc754e3a77edd98a7 48 | hooks: 49 | - id: validate-cff 50 | - repo: https://github.com/python-poetry/poetry 51 | rev: 1.8.3 52 | hooks: 53 | - id: poetry-check 54 | - id: poetry-install 55 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.9" 7 | 8 | formats: all 9 | 10 | sphinx: 11 | configuration: docs/conf.py 12 | 13 | python: 14 | install: 15 | - requirements: docs/requirements.txt 16 | -------------------------------------------------------------------------------- /.safety-policy.yml: -------------------------------------------------------------------------------- 1 | version: '2.0' 2 | 3 | # Safety Security and License Configuration file 4 | security: # configuration for the `safety check` command 5 | ignore-cvss-severity-below: 0 # A severity number between 0 and 10. Some helpful reference points: 9=ignore all vulnerabilities except CRITICAL severity. 7=ignore all vulnerabilities except CRITICAL 6 | ignore-cvss-unknown-severity: False # True or False. We recommend you set this to False. 7 | ignore-vulnerabilities: # Here you can list multiple specific vulnerabilities you want to ignore (optionally for a time period) 8 | # We recommend making use of the optional `reason` and `expires` keys for each vulnerability that you ignore. 9 | 70612: 10 | reason: we do not use the vulnerable function 11 | expires: '2025-10-10' 12 | continue-on-vulnerability-error: False # Suppress non-zero exit codes when vulnerabilities are found. Enable this in pipelines and CI/CD processes if you want to pass builds that have vulnerabilities 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | 8 | ## [Unreleased] 9 | 10 | ## [3.1.0] - 2024-11-16 11 | 12 | ### Added 13 | - `.safety-policy.yml` file. 14 | 15 | ### Changed 16 | - typer from `^0.9.0` to `^0.13.0`. 17 | - coverage from `^7.4.1` to `^7.6.7`. 18 | - pytest-cov from `^4.1.0` to `^6.0.0`. 19 | - sphinx from `^7.2.6` to `^7.4.7`. 20 | - black from `^24.2.0` to `^24.10.0`. 21 | - mypy from `^1.8.0` to `^1.13.0`. 22 | - ruff from `^0.2.1` to `^0.7.4`. 23 | - safety from `^3.0.1` to `^3.2.11`. 24 | - pytest from `^8.0.1` to `^8.3.3`. 25 | - pre-commit from `^3.6.1` to `^4.0.1`. 26 | - xdoctest from `^1.1.3` to `^1.2.0`. 27 | - watchdog from `^4.0.0` to `^6.0.0`. 28 | - poetry pre-commit hooks from `1.7.1` to `1.8.3`. 29 | - pre-commit-hooks from `v4.5.0` to `v5.0.0`. 30 | - codecov/codecov-action from `v3.1.4` to `v4.4.1`. 31 | - actions/upload-artifact from `v4` to `v4.4.3`. 32 | - actions/download-artifact from `v4` to `v4.1.8`. 33 | - actions/setup-python from `v5.0.0` to `v5.3.0`. 34 | - mindsers/changelog-reader-action from `v2.2.2` to `v2.2.3`. 35 | - peter-evans/create-pull-request from `v6.0.0` to `v7.0.5`. 36 | - actions/checkout from `v4.1.1` to `v4.2.2`. 37 | 38 | ### Fixed 39 | - Citation abstract to be generated with `project_short_description`. 40 | - Docs `conf.py` to the linting target path. 41 | - Release workflow permissions. 42 | - Release Pypi. 43 | - Nox coverage session under an interactive execution. 44 | 45 | ## [3.0.1] - 2024-02-18 46 | ### Fixed 47 | - Github funding file syntax. 48 | 49 | ## [3.0.0] - 2024-02-17 50 | ### Added 51 | - Ruff `^0.2.1`. 52 | - Python `3.12` support. 53 | - `CODEOWNERS` file. 54 | - `FUNDING.yml` file. 55 | - poetry pre-commit hooks. 56 | - Typer `^0.9.0`. 57 | - `py.typed` file. 58 | - `CITATION.cff` file. 59 | 60 | ### Changed 61 | - mypy from `^1.7.0` to `^1.8.0`. 62 | - actions/setup-python from `v4.7.1` to `v5.0.0`. 63 | - sphinx from `^7.1.2` to `^7.2.6`. 64 | - isort from `^5.12.0` to `^5.13.2`. 65 | - black from `^23.11.0` to `^24.2.0`. 66 | - pre-commit from `^3.5.0` to `^3.6.1`. 67 | - github/codeql-action from `v2` to `v3`. 68 | - coverage from `^7.3.2` to `^7.4.1`. 69 | - pypa/gh-action-pypi-publish from from `v1.8.10` to `v1.8.11`. 70 | - actions/upload-artifact from `v2.2.4` to `v4`. 71 | - actions/download-artifact from `v2.0.10` to `v4`. 72 | - Contributor Covenant from `v2.0` to `v2.1`. 73 | - pytest from `^7.4.3` to `^8.0.1`. 74 | - safety from `^2.4.0b2` to `^3.0.1`. 75 | - peter-evans/create-pull-request from `v5.0.2` to `v6.0.0`. 76 | - xdoctest from `^1.1.2` to `^1.1.3`. 77 | - watchdog from `^3.0.0` to `^4.0.0`. 78 | 79 | ### Removed 80 | - falke8 and flakeheaven. 81 | - darglint. 82 | - Python `3.8` support. 83 | - Click in favor of Typer. 84 | 85 | ### Fixed 86 | - template whitespace control. 87 | - docs typo. 88 | 89 | ## [2.3.1] - 2023-11-18 90 | ### Changed 91 | - actions/setup-python from `v4.6.1` to `v4.7.1`. 92 | - actions/checkout from `v3.5.2` to `v4.1.1`. 93 | - peter-evans/create-pull-request from `v5.0.1` to `v5.0.2`. 94 | - pypa/gh-action-pypi-publish from from `v1.4.2` to `v1.8.10`. 95 | - pre-commit-hooks from `v4.4.0` to `v4.5.0`. 96 | - sphinx from `^7.0.1` to `^7.1.2`. 97 | - invoke from `^2.1.2` to `^2.2.0`. 98 | - pre-commit from `^3.3.2` to `^3.5.0`. 99 | - black from `^23.3.0` to `^23.11.0`. 100 | - mypy from `^1.3.0` to `^1.7.0`. 101 | - safety from `^2.4.0b1` to `^2.4.0b2`. 102 | - pytest from `^7.3.1` to `^7.4.3`. 103 | - xdoctest from `^1.1.1` to `^1.1.2`. 104 | - coverage from `^7.2.6` to `^7.3.2`. 105 | - click from `^8.1.3` to `^8.1.7`. 106 | - flake8-builtins from `^2.1.0` to `^2.2.0`. 107 | 108 | ### Fixed 109 | - readthedocs config. 110 | 111 | ## [2.3.0] - 2023-05-28 112 | ### Changed 113 | - Reorganized dev dependencies into groups. 114 | - Renamed invoke safety task to invoke security. 115 | - flakeheaven pre-coomit hook with local deps. 116 | 117 | ## [2.2.1] - 2023-05-25 118 | ### Changed 119 | - Moved mypy configurations from `mypy.ini` to `pyproject.toml`. 120 | - Moved poetry `dev-dependencies` to `group.dev.dependencies`. 121 | - peter-evans/create-pull-request from `v4.2.3` to `v5.0.1`. 122 | - actions/checkout from `v3.3.0` to `v3.5.2`. 123 | - codecov/codecov-action from `v2.0.2` to `v3.1.4`. 124 | - actions/setup-python from `v4.5.0` to `v4.6.1`. 125 | - pytest-cov from `^3.0.0` to `^4.1.0`. 126 | - coverage from `^7.2.1` to `^7.2.6`. 127 | - flakeheaven from `^3.2.1` to `^3.3.0`. 128 | - black from `^23.1.0` to `^23.3.0`. 129 | - sphinx from `^6.1.3` to `^7.0.1`. 130 | - flake8-bugbear from `^23.2.13` to `^23.3.12`. 131 | - watchdog from `^2.3.1` to `^3.0.0`. 132 | - pytest-cookies from `^0.6.1` to `^0.7.0`. 133 | - pytest from `^7.2.2` to `^7.3.1`. 134 | - mypy from `^1.0.1` to `^1.3.0`. 135 | - pre-commit from `^3.1.1` to `^3.3.2`. 136 | - invoke from `^2.0.0` to `^2.1.2`. 137 | 138 | ### Fixed 139 | - Adjusted minimum target version for Black. 140 | 141 | ## [2.2.0] - 2023-03-09 142 | ### Added 143 | - Python `3.11` support. 144 | - nox-poetry. 145 | - flake8-broken-line `^0.6.0`. 146 | - flakeheaven `^3.2.1`. 147 | 148 | ### Changed 149 | - black from `^21.9b0` to `^23.1.0`. 150 | - flake8 from `^3.9.2` to `>=4.0.1,<5.0.0`. 151 | - checkout action from `v2.3.4` to `v2.4.0`. 152 | - darglint from `^1.8.0` to `^1.8.1`. 153 | - actions/setup-python from `v2.2.2` to `v4.5.0`. 154 | - actions/checkout from `v2.4.0` to `v3.3.0`. 155 | - github/codeql-action from `v1` to `v2`. 156 | - peter-evans/create-pull-request from `v3.10.1` to `v4.2.3`. 157 | - mindsers/changelog-reader-action from `v2.0.0` to `v2.2.2`. 158 | - pre-commit from `^2.15.0` to `^3.1.1`. 159 | - isort from `^5.9.3` to `^5.12.0`. 160 | - sphinx from `^4.2.0` to `^6.1.3`. 161 | - flake8-bugbear from `^21.9.2` to `^23.2.13`. 162 | - mypy from `^0.910` to `^1.0.1`. 163 | - pre-commit-hooks from `v4.0.1` to `v4.4.0`. 164 | - watchdog from `^2.1.6` to `^2.3.1`. 165 | - invoke from `^1.6.0` to `^2.0.0`. 166 | - pytest from `^6.2.5` to `^7.2.2`. 167 | - xdoctest from `^0.15.10` to `^1.0.0`. 168 | - flake8-bandit from `^2.1.2` to `^3.0.0`. 169 | - safety from `^1.10.3` to `^2.4.0b1`. 170 | - flake8-annotations from `^2.6.2` to `^2.9.1`. 171 | - flake8-blind-except from `^0.2.0` to `^0.2.1`. 172 | - flake8-logging-format from `^0.6.0` to `^0.9.0`. 173 | - xdoctest from `^1.0.0` to `^1.1.1`. 174 | - coverage from `^6.0.1` to `^7.2.1`. 175 | - flake8-docstrings from `1.6.0` to `1.7.0`. 176 | - flake8-builtins from `1.5.3` to `2.1.0`. 177 | - click from `^8.0.3` to `^8.1.3`. 178 | 179 | ### Removed 180 | - Python `3.7` support. 181 | - flakehell `^0.9.0`. 182 | 183 | ## [2.1.0] - 2021-10-12 184 | ### Added 185 | - Python `3.10` support. 186 | 187 | ### Changed 188 | - pre-commit from `^2.13.0` to `^2.15.0`. 189 | - watchdog from `^2.1.3` to `^2.1.6`. 190 | - flake8-bugbear from `^21.4.3` to `^21.9.2`. 191 | - xdoctest from `^0.15.5` to `^0.15.10`. 192 | - sphinx from `^4.1.1` to `^4.2.0`. 193 | - black from `^21.7b0` to `^21.9b0`. 194 | - pytest from `^6.2.4` to `^6.2.5`. 195 | - isort from `^5.9.2` to `^5.9.3`. 196 | - peter-evans/create-pull-request from `v3.10.0` to `v3.10.1`. 197 | - coverage from `^5.3` to `^6.0.1`. 198 | - pytest-cov from `^2.10.1` to `^3.0.0`. 199 | - click from `^8.0.1` to `^8.0.3`. 200 | 201 | ## [2.0.1] - 2021-07-26 202 | ### Fixed 203 | - package discovery when `project_name` != `project_slug`. 204 | - `end-of-file` hook. 205 | - pypi release step only for open source projects. 206 | 207 | ### Changed 208 | - isort from `^5.8.0` to `^5.9.2`. 209 | - watchdog from `^2.1.2` to `^2.1.3`. 210 | - xdoctest from `^0.15.4` to `^0.15.5`. 211 | - mypy from `^0.902` to `^0.910`. 212 | - sphinx from `^4.0.2` to `^4.1.1`. 213 | - invoke from `^1.5.0` to `^1.6.0`. 214 | - actions/upload-artifact from `v2.2.0` to `v2.2.4`. 215 | - actions/download-artifact from `v2.0.5` to `v2.0.10`. 216 | - codecov/codecov-action from `v1.3.1` to `v2.0.2`. 217 | - pypa/gh-action-pypi-publish for `v1.4.1` to `v1.4.2`. 218 | 219 | ## [2.0.0] - 2021-06-12 220 | ### Added 221 | - optional `CODE_OF_CONDUCT.md` file. 222 | - optional `CONTRIBUTING.md` file. 223 | - optional `SECURITY.md` file. 224 | - multiple issue templates config. 225 | - codeql analysis workflow. 226 | - pre-commit autoupdate workflow. 227 | 228 | ### Changed 229 | - sphinx from `3.5.4` to `4.0.2`. 230 | - watchdog from `2.0.3` to `2.1.2`. 231 | - flake8 from `3.9.1` to `3.9.2`. 232 | - pytest from `6.2.3` to `6.2.4`. 233 | - pre-commit from `^2.12.1` to `^2.13.0`. 234 | - pre-commit-hooks from `^3.0.0` to `^4.0.1`. 235 | - black from `^20.8b1` to `^21.5b2`. 236 | - click from `^7.1.2` to `^8.0.1`. 237 | - mypy from `^0.812` to `^0.902`. 238 | 239 | ### Removed 240 | - Python `3.6` support. 241 | 242 | ## [1.2.3] - 2021-04-27 243 | ### Changed 244 | - sphinx from `3.5.2` to `3.5.4`. 245 | - flake8 from `3.8.4` to `3.9.1`. 246 | - isort from `5.7.0` to `5.8.0`. 247 | - flake8-bugbear from `21.3.2` to `21.4.3`. 248 | - flake8-annotations from `2.6.1` to `2.6.2`. 249 | - darglint from `1.7.0` to `1.8.0`. 250 | - pytest from `6.2.2` to `6.2.3`. 251 | - watchdog from `^2.0.2` to `^2.0.3`. 252 | - pre-commit from `^2.11.1` to `^2.12.1`. 253 | - actions/setup-python from `v2.2.1` to `v2.2.2`. 254 | - pre-commit flakehell additional_dependencies versions config. 255 | 256 | ### Fixed 257 | - flakehell config for flake8 >= 3.9.1. 258 | 259 | ## [1.2.2] - 2021-03-19 260 | ### Changed 261 | - xdoctest from `0.15.2` to `0.15.4`. 262 | - pre-commit from `^2.9.3` to `^2.11.1`. 263 | - pytest from `^6.2.1` to `^6.2.2`. 264 | - mypy from `^0.790` to `^0.812`. 265 | - watchdog from `^1.0.2` to `^2.0.2`. 266 | - sphinx from `^3.4.3` to `^3.5.2`. 267 | - darglint from `^1.5.8` to `^1.7.0`. 268 | - flake8-annotations from `^2.5.0` to `^2.6.1`. 269 | - flake8-bugbear from `^20.11.1` to `^21.3.2`. 270 | - flake8-docstrings from `1.5.0` to `1.6.0`. 271 | - codecov/codecov-action from `v1.0.13` to `v1.3.1`. 272 | 273 | ### Fixed 274 | - bump2version invoke command. 275 | 276 | ## [1.2.1] - 2021-01-23 277 | ### Changed 278 | - xdoctest from `0.15.0` to `0.15.2`. 279 | 280 | ### Fixed 281 | - bump2version config file. 282 | 283 | ## [1.2.0] - 2021-01-17 284 | ### Added 285 | - pyproject `documentation` entry. 286 | - pyproject `tool.poetry.urls` section. 287 | 288 | ### Fixed 289 | - Readme links. 290 | 291 | ### Changed 292 | - sphinx from `^3.4.0` to `^3.4.3`. 293 | - safety from `^1.10.0` to `^1.10.3`. 294 | - flake8-blind-except from `^0.1.1` to `^0.2.0`. 295 | - flake8-annotations from `^2.1.0` to `^2.5.0`. 296 | - isort from `^5.6.4` to `^5.7.0`. 297 | - invoke from `^1.4.1` to `^1.5.0`. 298 | - flakehell from `^0.7.1` to `^0.9.0`. 299 | - parametrize cli tests. 300 | 301 | ## [1.1.3] - 2020-12-23 302 | ### Changed 303 | - sphinx from `^3.3.0` to `^3.4.0`. 304 | - recommonmark from `0.6.0` to `0.7.1`. 305 | - watchdog from `^0.10.2` to `^1.0.2`. 306 | - pre-commit from `^2.8.2` to `^2.9.3`. 307 | - flakehell from `^0.7.0` to `^0.7.1`. 308 | - safety from `^1.9.0` to `^1.10.0`. 309 | - darglint from `^1.3.0` to `^1.5.8`. 310 | - flake8-bugbear from `^20.1.4` to `^20.11.1`. 311 | - actions/setup-python from `v2.1.4` to `v2.2.1`. 312 | - pytest from `^6.1.2` to `^6.2.1`. 313 | 314 | ## [1.1.2] - 2020-11-07 315 | ### Changed 316 | - flakehell from `^0.6.1` to `^0.7.0`. 317 | - create-release action from `v1` to `v1.1.4`. 318 | - checkout action from `v2` to `v2.3.4`. 319 | - setup-python action from `v2` to `v2.1.4`. 320 | - sphinx from `^3.2.1` to `^3.3.0`. 321 | - pre-commit from `^2.7.1` to `^2.8.2`. 322 | - pytest from `^6.1.1` to `^6.1.2`. 323 | 324 | ### Fixes 325 | - mypy nox session requirements. 326 | 327 | ## [1.1.1] - 2020-10-18 328 | ### Fixes 329 | - `docs/conf.py` imports. 330 | - coverage config. 331 | 332 | ## [1.1.0] - 2020-10-17 333 | ### Changed 334 | - to `src` structure. 335 | - `poject_name` validation. 336 | 337 | ### Added 338 | - `project_title`. 339 | 340 | ## [1.0.1] - 2020-10-15 341 | ### Fixed 342 | - unnecessary `validation_depth` on `mindsers/changelog-reader-action`. 343 | 344 | ## [1.0.0] - 2020-10-15 345 | ### Added 346 | - License section on the docs. 347 | - Codecov integration. 348 | - PyPI and TestPyPI steps on the release workflow. 349 | - Python `3.9` support. 350 | 351 | ### Changed 352 | - github actions ready to configure activity types. 353 | - isort from `^5.5.4` to `^5.6.4`. 354 | - bump2version from `master` to `^1.0.1`. 355 | - mypy from `^0.782` to `^0.790`. 356 | - coverage from `^5.1` to `^6.0.1`. 357 | - pytest-cov from `^2.8.1` to `^2.10.1`. 358 | - pytest from `^5.4.2` to `^6.1.1`. 359 | - flake8 from `^3.7.9` to `^3.8.4`. 360 | 361 | ### Fixed 362 | - missing pre-commit requirement. 363 | - get release version on the release workflow. 364 | 365 | ## [0.2.1] - 2020-10-05 366 | ### Changed 367 | - changelog-reader-action from v1.1.0 to v2. 368 | - sphinx from 3.0.4 to 3.2.1. 369 | - flakehell from 0.3.6 to 0.6.1. 370 | - black from 19.10b0 to 20.8b1. 371 | - xdoctest from 0.12.0 to 0.15.0. 372 | - mypy from 0.770 to 0.782 373 | 374 | ### Fixed 375 | - read the docs dependencies. 376 | 377 | ## [0.2.0] - 2020-10-04 378 | ### Added 379 | - Dependabot configuration. 380 | - Safety session to nox. 381 | - Safety step to the test workflow. 382 | 383 | ### Changed 384 | - flake8 version to `^3.7.9`. 385 | - isort version to `^5.5.4`. 386 | - poetry export without hashes on the noxfiles. 387 | 388 | ### Removed 389 | - Pyup.io integration. 390 | - seed-isort-config from the pre-commit-config. 391 | 392 | ### Fixed 393 | - docs/readme.md symbolic link to README.md. 394 | - docs/changelog.md symbolic link to CHANGELOG.md. 395 | - missing badges. 396 | 397 | ## [0.1.4] - 2020-09-07 398 | ### Changed 399 | - Python actions to the v2. 400 | 401 | ### Removed 402 | - Unnecessary python steps on the release workflow. 403 | 404 | ### Fixed 405 | - bump2version version. 406 | 407 | ## [0.1.3] - 2020-08-13 408 | ### Fixed 409 | - isort support for pyproject.toml 410 | - docs conf code style. 411 | 412 | ### Removed 413 | - sphinx-autodoc-typehints from the dev requirements. 414 | 415 | ## [0.1.2] - 2020-06-14 416 | ### Fixed 417 | - Read the docs build config. 418 | 419 | ### Removed 420 | - Pytype from the dev requirements. 421 | 422 | ## [0.1.1] - 2020-06-14 423 | ### Added 424 | - New option `serve` to the invoke docs task. 425 | 426 | ### Changed 427 | - Improve docs tutorial section. 428 | - Improve docs index section. 429 | 430 | ### Fixed 431 | - README spelling. 432 | - Ivoke pytype task typo. 433 | 434 | ## [0.1.0] - 2020-06-11 435 | ### Added 436 | - First release. 437 | 438 | 439 | [Unreleased]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v3.1.0...develop 440 | [3.1.0]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v3.0.1...v3.1.0 441 | [3.0.1]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v3.0.0...v3.0.1 442 | [3.0.0]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v2.3.1...v3.0.0 443 | [2.3.1]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v2.3.0...v2.3.1 444 | [2.3.0]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v2.2.1...v2.3.0 445 | [2.2.1]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v2.2.0...v2.2.1 446 | [2.2.0]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v2.1.0...v2.2.0 447 | [2.1.0]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v2.0.1...v2.1.0 448 | [2.0.1]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v2.0.0...v2.0.1 449 | [2.0.0]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v1.2.3...v2.0.0 450 | [1.2.3]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v1.2.2...v1.2.3 451 | [1.2.2]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v1.2.1...v1.2.2 452 | [1.2.1]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v1.2.0...v1.2.1 453 | [1.2.0]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v1.1.3...v1.2.0 454 | [1.1.3]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v1.1.2...v1.1.3 455 | [1.1.2]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v1.1.1...v1.1.2 456 | [1.1.1]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v1.1.0...v1.1.1 457 | [1.1.0]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v1.0.1...v1.1.0 458 | [1.0.1]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v1.0.0...v1.0.1 459 | [1.0.0]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v0.2.1...v1.0.0 460 | [0.2.1]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v0.2.0...v0.2.1 461 | [0.2.0]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v0.1.4...v0.2.0 462 | [0.1.4]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v0.1.3...v0.1.4 463 | [0.1.3]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v0.1.2...v0.1.3 464 | [0.1.2]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v0.1.1...v0.1.2 465 | [0.1.1]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/v0.1.0...v0.1.1 466 | [0.1.0]: https://github.com/fedejaure/cookiecutter-modern-pypackage/compare/releases/tag/v0.1.0 467 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | # This CITATION.cff file was generated with cffinit. 2 | # Visit https://bit.ly/cffinit to generate yours today! 3 | 4 | cff-version: 1.2.0 5 | title: Cookiecutter Modern PyPackage 6 | message: >- 7 | If you use this software, please cite it using the 8 | metadata from this file. 9 | type: software 10 | authors: 11 | - given-names: Federico 12 | family-names: Jaureguialzo 13 | alias: fedejaure 14 | identifiers: 15 | - type: url 16 | value: >- 17 | https://github.com/fedejaure/cookiecutter-modern-pypackage/releases/tag/v3.1.0 18 | description: The Software Heritage link for version 3.1.0. 19 | repository-code: 'https://github.com/fedejaure/cookiecutter-modern-pypackage' 20 | abstract: Cookiecutter template for a modern Python package. 21 | keywords: 22 | - cookiecutter 23 | - template 24 | - package 25 | license: MIT 26 | version: 3.1.0 27 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official email address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at @fedejaure. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series of 86 | actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or permanent 93 | ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within the 113 | community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.1, available at 119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 126 | [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 130 | [Mozilla CoC]: https://github.com/mozilla/diversity 131 | [FAQ]: https://www.contributor-covenant.org/faq 132 | [translations]: https://www.contributor-covenant.org/translations 133 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Cookiecutter Modern PyPackage 2 | 3 | 👏🎉 First off all, Thanks for your interest in contributing to our project! 🎉👏 4 | 5 | The following is a set of guidelines for contributing to Cookiecutter Modern PyPackage. These are 6 | mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. 7 | 8 | ## Code of Conduct 9 | 10 | We take our open source community seriously and hold ourselves and other contributors to high standards of communication. By participating and contributing to this project, you agree to uphold our [Code of Conduct](CODE_OF_CONDUCT.md). 11 | 12 | ## Getting Started 13 | 14 | ### Requirements 15 | 16 | We use `poetry` to manage and install dependencies. [Poetry](https://python-poetry.org/) provides a custom installer that will install `poetry` isolated from the rest of your system. 17 | 18 | ``` 19 | curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | python - 20 | ``` 21 | 22 | We'll also need `nox` for automated testing in multiple Python environments so [install that too](https://nox.thea.codes/en/stable/). 23 | 24 | To install the local development requirements inside a virtual environment run: 25 | 26 | ``` 27 | $ poetry install 28 | $ poetry run inv install-hooks 29 | ``` 30 | 31 | > For more information about `poetry` check the [docs](https://python-poetry.org/docs/). 32 | 33 | We use [invoke](http://www.pyinvoke.org/) to wrap up some useful tasks like formatting, linting, testing and more. 34 | 35 | Execute `inv[oke] --list` to see the list of available commands. 36 | 37 | ## Contributing 38 | 39 | ### Issues 40 | 41 | We use GitHub issues to track public bugs/enhancements. Report a new one by [opening a new issue](https://github.com/fedejaure/cookiecutter-modern-pypackage/issues). 42 | 43 | In this repository, we provide a couple of templates for you to fill in for: 44 | 45 | * Bugs 46 | * Feature Requests/Enhancements 47 | 48 | Please read each section in the templates and provide as much information as you can. Please do not put any sensitive information, 49 | such as personally identifiable information, connection strings or cloud credentials. The more information you can provide, the better we can help you. 50 | 51 | ### Pull Requests 52 | 53 | Please follow these steps to have your contribution considered by the maintainers: 54 | 55 | 1. Fork the repo and create your branch from `develop` locally with a succinct but descriptive name. 56 | 2. Add tests for the new changes 57 | 3. Edit documentation if you have changed something significant 58 | 4. Make sure to follow the [styleguides](#styleguides) 59 | 5. Open a PR in our repository and follow the PR template so that we can efficiently review the changes 60 | 6. After you submit your pull request, verify that all status checks are passing 61 | 62 | While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design 63 | work, tests, or other changes before your pull request can be ultimately accepted. 64 | 65 | ## Styleguides 66 | 67 | ### Python Code Style 68 | 69 | All Python code is linted with [Ruff](https://github.com/astral-sh/ruff) and formated with 70 | [Isort](https://github.com/PyCQA/isort) and [Black](https://github.com/psf/black). You can 71 | execute `inv[oke] lint` and `inv[oke] format`. 72 | 73 | ## Additional Notes 74 | 75 | If you have any question feel free to contact us at fedejaure@gmail.com. 76 | -------------------------------------------------------------------------------- /LICENSE.rst: -------------------------------------------------------------------------------- 1 | MIT License 2 | =========== 3 | 4 | Copyright (c) 2020 Federico Jaureguialzo 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cookiecutter Modern PyPackage 🐍📦 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/fedejaure/cookiecutter-modern-pypackage?logo=github)](https://github.com/fedejaure/cookiecutter-modern-pypackage/releases) 10 | [![Python Version](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12-blue?logo=python)](https://www.python.org/) 11 | [![Tests](https://github.com/fedejaure/cookiecutter-modern-pypackage/workflows/tests/badge.svg)](https://github.com/fedejaure/cookiecutter-modern-pypackage/actions?workflow=tests) 12 | [![Read the Docs](https://readthedocs.org/projects/cookiecutter-modern-pypackage/badge/)](https://cookiecutter-modern-pypackage.readthedocs.io/) 13 | [![License](https://img.shields.io/badge/license-MIT-brightgreen)](https://opensource.org/licenses/MIT) 14 | 15 | [![Black](https://img.shields.io/badge/code%20style-black-000000)](https://github.com/psf/black) 16 | [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) 17 | [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) 18 | 19 |
20 | 21 | [Cookiecutter][cookiecutter] 🍪 template for a modern Python package 🐍📦. 22 | 23 | * GitHub repo: 24 | * Documentation: 25 | * Free software: MIT license 26 | 27 | ## 🚀 Features 28 | 29 | * **Dependency tracking:** 📦 Utilizes [Poetry][poetry] for efficient package management. 30 | * **Testing setup:** 🧪 Includes [Pytest][pytest] for comprehensive and reliable testing. 31 | * **Continuous Integration:** 🔄 [Github Actions][github_actions] integration for seamless CI testing. 32 | * **Linting:** 🧹 Enhanced code quality with [Ruff][ruff]. 33 | * **Docstring:** 📚 Follows the [Google Python Style Guide][google_styleguide] for clear and consistent documentation. 34 | * **Static type checking:** 🔍 Ensured by [Mypy][mypy]. 35 | * **Formatting:** ✨ Consistent code formatting with [Black][black] and [Isort][isort]. 36 | * **Security checks:** 🔐 Uses [Safety][safety] to identify and address known vulnerabilities. 37 | * **Git hooks:** 🎣 Managed by [pre-commit][pre-commit] for streamlined development workflows. 38 | * **Development tasks CLI:** 🛠️ All-in-one Python CLI provided by [invoke][invoke]. 39 | * **Multiple Python environments testing:** 🥁 Supported by [Nox][nox]. 40 | * **Documentation:** 📖 Utilizes [Sphinx][sphinx] for clear and comprehensive documentation, ready for [Read the Docs][rtd]. 41 | * **Command line interface:** 💻 Optional integration with [Typer][typer]. 42 | * **Automated dependency updates:** 🤖 Enabled by [Dependabot][dependabot]. 43 | * **Coverage reports:** 📊 Integrated with [Codecov][codecov]. 44 | * **Automated releases:** 🚢 Push a new tag to trigger releases to [PyPI][pypi] and [TestPyPI][testpypi]. 45 | * **GitHub community health files (optional):** 46 | - [Code of Condunct][CODE_OF_CONDUCT.md] 🤝 47 | - [Contributing][CONTRIBUTING.md] 📝 48 | - [Security][SECURITY.md] 🔒 49 | - [Code Owners][CODEOWNERS] 👩‍💼 50 | - [Funding][FUNDING.yml] 💰 51 | - [Citation][CITATION.cff] 📑 52 | 53 | ## ⚡️ Quickstart 54 | 55 | Get started with your modern Python package in just a few steps: 56 | 57 | ### 1. Install Cookiecutter 58 | 59 | If you haven't installed Cookiecutter yet, make sure to have version 1.4.0 or higher: 60 | 61 | ```sh 62 | pip install -U cookiecutter 63 | ``` 64 | 65 | ### 2. Generate your Python Package 66 | 67 | Run Cookiecutter using the latest release 68 | 69 | ```sh 70 | cookiecutter gh:fedejaure/cookiecutter-modern-pypackage --checkout v3.1.0 71 | ``` 72 | 73 | ### 3. Set up Your Project 74 | 75 | Follow these steps to complete the setup: 76 | 77 | * Create a new GitHub repository and push your generated project there. 78 | * Install the development requirements into a virtual environment: 79 | 80 | ```sh 81 | poetry install 82 | ``` 83 | 84 | * Install pre-commit hooks: 85 | 86 | ```sh 87 | poetry run inv install-hooks 88 | ``` 89 | 90 | * Configure [Codecov][codecov] repository settings. (Codecov App, `CODECOV_TOKEN`) 91 | * Add your repository to your [Read the Docs][rtd] account and enable the Read the Docs service hook. 92 | * Configure [PyPI][pypi] and [TestPyPI][testpypi] tokens. (`PYPI_TOKEN`, `TEST_PYPI_TOKEN`) 93 | * Release your package by pushing a new tag. 94 | 95 | > [!TIP] 96 | > For more details, see the [tutorial][tutorial]. 97 | 98 | ## 📝 Credits 99 | 100 | This cookiecutter was built for learning purpose and inspired by: 101 | 102 | * [audreyr/cookiecutter-pypackage][audreyr/cookiecutter-pypackage]: Cookiecutter template for a Python package. 103 | * [briggySmalls/cookiecutter-pypackage][briggySmalls/cookiecutter-pypackage]: A fork from [audreyr/cookiecutter-pypackage][audreyr/cookiecutter-pypackage] using Poetry for package management, with linting, formatting and more. 104 | * [hypermodern-python][hypermodern-python]: Hypermodern Python article series. 105 | 106 | [cookiecutter]: https://github.com/cookiecutter/cookiecutter 107 | [poetry]: https://python-poetry.org/ 108 | [pytest]: https://github.com/pytest-dev/pytest 109 | [github_actions]: https://github.com/features/actions 110 | [ruff]: https://github.com/astral-sh/ruff 111 | [isort]: https://github.com/timothycrosley/isort 112 | [black]: https://github.com/psf/black 113 | [mypy]: https://github.com/python/mypy 114 | [pre-commit]: https://pre-commit.com/ 115 | [safety]: https://github.com/pyupio/safety 116 | [google_styleguide]: https://google.github.io/styleguide/pyguide.html 117 | [invoke]: https://www.pyinvoke.org/ 118 | [sphinx]: https://www.sphinx-doc.org/en/master/ 119 | [rtd]: https://readthedocs.org/ 120 | [nox]: https://nox.thea.codes/en/stable/ 121 | [tutorial]: https://cookiecutter-modern-pypackage.readthedocs.io/en/latest/tutorial.html 122 | [typer]: https://typer.tiangolo.com/ 123 | [dependabot]: https://dependabot.com/ 124 | [audreyr/cookiecutter-pypackage]: https://github.com/audreyr/cookiecutter-pypackage 125 | [briggySmalls/cookiecutter-pypackage]: https://github.com/briggySmalls/cookiecutter-pypackage 126 | [hypermodern-python]: https://cjolowicz.github.io/posts/hypermodern-python-01-setup/ 127 | [codecov]: https://codecov.io/ 128 | [pypi]: https://pypi.org/ 129 | [testpypi]: https://test.pypi.org/ 130 | [contributor-covenant]: https://www.contributor-covenant.org/ 131 | [CODE_OF_CONDUCT.md]: https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/adding-a-code-of-conduct-to-your-project 132 | [CONTRIBUTING.md]: https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors 133 | [SECURITY.md]: https://docs.github.com/en/code-security/getting-started/adding-a-security-policy-to-your-repository 134 | [CODEOWNERS]: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners 135 | [FUNDING.yml]: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository 136 | [CITATION.cff]: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-citation-files 137 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | We takes the security of our software products seriously. 4 | 5 | If you believe you have found a security vulnerability, please report it to us as described below. 6 | 7 | ## Reporting Security Issues 8 | 9 | **Please do not report security vulnerabilities through public GitHub issues.** 10 | 11 | Report security vulnerabilities by emailing the lead maintainer at fedejaure@gmail.com. 12 | 13 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. 14 | 15 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 16 | 17 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 18 | * Full paths of source file(s) related to the manifestation of the issue 19 | * The location of the affected source code (tag/branch/commit or direct URL) 20 | * Any special configuration required to reproduce the issue 21 | * Step-by-step instructions to reproduce the issue 22 | * Proof-of-concept or exploit code (if possible) 23 | * Impact of the issue, including how an attacker might exploit the issue 24 | 25 | This information will help us triage your report more quickly. 26 | 27 | ## Preferred Languages 28 | 29 | We prefer all communications to be in English. 30 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedejaure/cookiecutter-modern-pypackage/d4e1d7bb0c5374beaf38314f7471f44ae81bf48d/assets/logo.png -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "given_names": "Federico", 3 | "family_names": "Jaureguialzo", 4 | "full_name": "{{ cookiecutter.given_names }} {{ cookiecutter.family_names }}", 5 | "email": "fedejaure@example.com", 6 | "github_username": "fedejaure", 7 | "project_name": "modern-python-boilerplate", 8 | "project_slug": "{{ cookiecutter.project_name.lower().replace('-', '_') }}", 9 | "project_title": "{{ cookiecutter.project_name.replace('-', ' ').title() }}", 10 | "project_short_description": "Python Boilerplate contains all the boilerplate you need to create a modern Python package.", 11 | "version": "0.1.0", 12 | "open_source_license": [ 13 | "MIT", 14 | "BSD", 15 | "ISC", 16 | "Apache Software License 2.0", 17 | "GNU General Public License v3", 18 | "Not open source" 19 | ], 20 | "command_line_interface": [ 21 | "Typer", 22 | "No command-line interface" 23 | ], 24 | "add_code_of_conduct": "y", 25 | "add_contributing_file": "y", 26 | "add_security_file": "y", 27 | "add_codeowners_file": "y", 28 | "add_funding_file": "y", 29 | "add_citation_file": "y", 30 | "contact_method": null 31 | } 32 | -------------------------------------------------------------------------------- /docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | .logo a { overflow-wrap: normal } 2 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | ../CHANGELOG.md -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | """Sphinx configuration.""" 2 | 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file only contains a selection of the most common options. For a full 6 | # list see the documentation: 7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 8 | 9 | 10 | # -- Project information ----------------------------------------------------- 11 | 12 | project = "cookiecutter-modern-pypackage" 13 | copyright = "2020, Federico Jaureguialzo" # noqa: A001 14 | author = "Federico Jaureguialzo" 15 | 16 | # The version info for the project you're documenting, acts as replacement for 17 | # |version| and |release|, also used in various other places throughout the 18 | # built documents. 19 | # 20 | # The short X.Y version. 21 | version = "3.1.0" 22 | # The full version, including alpha/beta/rc tags. 23 | release = "3.1.0" 24 | 25 | # -- General configuration --------------------------------------------------- 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be 28 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 29 | # ones. 30 | extensions = [ 31 | "sphinx.ext.autodoc", 32 | "sphinx.ext.viewcode", 33 | "sphinx.ext.napoleon", 34 | "recommonmark", 35 | ] 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ["_templates"] 39 | 40 | # List of patterns, relative to source directory, that match files and 41 | # directories to ignore when looking for source files. 42 | # This pattern also affects html_static_path and html_extra_path. 43 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 44 | 45 | 46 | # -- Options for HTML output ------------------------------------------------- 47 | 48 | # The theme to use for HTML and HTML Help pages. See the documentation for 49 | # a list of builtin themes. 50 | # 51 | html_theme = "alabaster" 52 | 53 | # Theme options are theme-specific and customize the look and feel of a theme 54 | # further. For a list of options available for each theme, see the 55 | # documentation. 56 | html_theme_options = { 57 | "github_user": "fedejaure", 58 | "github_repo": "cookiecutter-modern-pypackage", 59 | "github_banner": True, 60 | "show_related": False, 61 | "fixed_sidebar": True, 62 | } 63 | 64 | # Add any paths that contain custom static files (such as style sheets) here, 65 | # relative to this directory. They are copied after the builtin static files, 66 | # so a file named "default.css" will overwrite the builtin "default.css". 67 | html_static_path = ["_static", "../assets/logo.png"] 68 | -------------------------------------------------------------------------------- /docs/console_script_setup.rst: -------------------------------------------------------------------------------- 1 | .. _console-script-setup: 2 | 3 | 4 | Console Script Setup 5 | ==================== 6 | 7 | Optionally, your package can include a console script using Typer (Python 3.9+). 8 | 9 | How It Works 10 | ------------ 11 | 12 | If the 'command_line_interface' option is set to ['typer'] during setup, cookiecutter will 13 | add a file 'cli.py' in the project_slug subdirectory. An entry point is added to 14 | pyproject.toml that points to the main function in cli.py. 15 | 16 | Usage 17 | ----- 18 | To use the console script in development: 19 | 20 | .. code-block:: bash 21 | 22 | pip install -e projectdir 23 | 24 | 'projectdir' should be the top level project directory with the pyproject.toml file 25 | 26 | The script will be generated with output for no arguments and --help. 27 | 28 | --help 29 | show help menu and exit 30 | 31 | More Details 32 | ------------ 33 | 34 | You can read more about Typer at: 35 | https://typer.tiangolo.com/ 36 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to cookiecutter-modern-pypackage's documentation! 2 | ========================================================= 3 | 4 | Getting Started 5 | --------------- 6 | 7 | .. toctree:: 8 | :maxdepth: 2 9 | 10 | readme 11 | tutorial 12 | changelog 13 | 14 | Basics 15 | ------ 16 | 17 | .. toctree:: 18 | :maxdepth: 2 19 | 20 | prompts 21 | invoke 22 | 23 | Advanced Features 24 | ----------------- 25 | 26 | .. toctree:: 27 | :maxdepth: 2 28 | 29 | console_script_setup 30 | 31 | .. toctree:: 32 | :hidden: 33 | 34 | License 35 | -------------------------------------------------------------------------------- /docs/invoke.rst: -------------------------------------------------------------------------------- 1 | Invoke 2 | ====== 3 | 4 | The generated project is ready to run some useful tasks like formatting, linting, testing and more. 5 | 6 | To do this we use pyinvoke_ to wrap up the required commands. 7 | 8 | .. _pyinvoke: http://www.pyinvoke.org/ 9 | 10 | Execute `inv[oke] --list` to see the list of available commands. 11 | 12 | .. code-block:: bash 13 | 14 | $ poerty shell 15 | $ inv[oke] --list 16 | Available tasks: 17 | 18 | clean Run all clean sub-tasks. 19 | clean-build Clean up files from package building. 20 | clean-docs Clean up files from documentation builds. 21 | clean-python Clean up python file artifacts. 22 | clean-tests Clean up files from testing. 23 | coverage Create coverage report. 24 | docs Build documentation. 25 | format Format code. 26 | hooks Run pre-commit hooks. 27 | install-hooks Install pre-commit hooks. 28 | lint Run all linting. 29 | mypy Run mypy. 30 | ruff Run ruff. 31 | security Run security related checks. 32 | tests Run tests. 33 | version Bump version. 34 | -------------------------------------------------------------------------------- /docs/license.rst: -------------------------------------------------------------------------------- 1 | ../LICENSE.rst -------------------------------------------------------------------------------- /docs/prompts.rst: -------------------------------------------------------------------------------- 1 | Prompts 2 | ======= 3 | 4 | When you create a package, you are prompted to enter these values. 5 | 6 | Templated Values 7 | ---------------- 8 | 9 | The following appear in various parts of your generated project. 10 | 11 | given_names 12 | Your given names. 13 | 14 | family_names 15 | Your family names. 16 | 17 | full_name 18 | Your full name. Typically, it is the combination of your given and family names. 19 | 20 | email 21 | Your email address. 22 | 23 | github_username 24 | Your GitHub username. 25 | 26 | project_name 27 | The name of your new Python package project. This is used in the package name and the Github repository name, so use - insteed of spaces. 28 | 29 | project_slug 30 | The namespace of your Python package. This should be Python import-friendly. Typically, it is the slugified version of project_name. 31 | 32 | project_title 33 | The title of your new Python project. This is used in documentation, so spaces and any characters are fine here. 34 | 35 | project_short_description 36 | A 1-sentence description of what your Python package does. 37 | 38 | version 39 | The starting version number of the package. 40 | 41 | Options 42 | ------- 43 | 44 | The following package configuration options set up different features for your project. 45 | 46 | open_source_license 47 | Whether to add a license file. Options: ["MIT", "BSD", "ISC", "Apache Software License 2.0", "GNU General Public License v3", "Not open source"s] 48 | 49 | command_line_interface 50 | Whether to create a console script using Typer. Console script entry point will match the project_name. Options: ["Typer", "No command-line interface"] 51 | 52 | add_code_of_conduct 53 | Whether to add a Contributor Covenant Code of Conduct file. 54 | 55 | add_contributing_file 56 | Whether to add a Contributing Guide file. 57 | 58 | add_security_file 59 | Whether to add a Security Policy file. 60 | 61 | add_codeowners_file 62 | Whether to add a `CODEOWNERS` file. 63 | 64 | add_funding_file 65 | Whether to add a `FUNDING.yml` file. 66 | 67 | add_citation_file 68 | Whether to add a `CITATION.cff` file. 69 | 70 | contact_method 71 | Whether to add a contact method. Used on files such of `CODE_OF_CONDUCT.md`, `CONTRIBUTING.md` and `SECURITY.md`. 72 | -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx==7.4.7 2 | recommonmark==0.7.1 3 | -------------------------------------------------------------------------------- /docs/tutorial.rst: -------------------------------------------------------------------------------- 1 | Tutorial 2 | ======== 3 | 4 | .. note:: Did you find any of these instructions confusing? `Edit this file`_ 5 | and submit a pull request with your improvements! 6 | 7 | .. _`Edit this file`: https://github.com/fedejaure/cookiecutter-modern-pypackage/blob/master/docs/tutorial.rst 8 | 9 | To start with, you will need a `GitHub account`_ and an account on `PyPI`_. Create these before you get started on this tutorial. If you are new to Git and GitHub, you should probably spend a few minutes on some of the tutorials at the top of the page at `GitHub Help`_. 10 | 11 | .. _`GitHub account`: https://github.com/ 12 | .. _`PyPI`: https://pypi.python.org/pypi 13 | .. _`GitHub Help`: https://help.github.com/ 14 | 15 | 16 | Step 1: Install Cookiecutter 17 | ---------------------------- 18 | 19 | Install cookiecutter: 20 | 21 | .. code-block:: bash 22 | 23 | $ pip install cookiecutter 24 | 25 | We'll also need poetry so `install that too`_. 26 | 27 | .. _`install that too`: https://python-poetry.org/docs/#installation 28 | 29 | Step 2: Generate Your Package 30 | ----------------------------- 31 | 32 | Now it's time to generate your Python package. 33 | 34 | Use cookiecutter, pointing it at the cookiecutter-pypackage repo: 35 | 36 | .. code-block:: bash 37 | 38 | $ cookiecutter gh:fedejaure/cookiecutter-modern-pypackage --checkout v3.1.0 39 | 40 | You'll be asked to enter a bunch of values to set the package up. 41 | If you don't know what to enter, stick with the defaults. 42 | 43 | 44 | Step 3: Create a GitHub Repo 45 | ---------------------------- 46 | 47 | Go to your GitHub account and create a new repo named ``mypackage``, where ``mypackage`` matches the ``[project_name]`` from your answers to running cookiecutter. 48 | 49 | You will find one folder named after the ``[project_name]``. Move into this folder, and then setup git to use your GitHub repo and upload the code: 50 | 51 | .. code-block:: bash 52 | 53 | $ cd mypackage 54 | mypackage $ git init . 55 | mypackage $ git add . 56 | mypackage $ git commit -m "Initial skeleton." 57 | mypackage $ git remote add origin git@github.com:myusername/mypackage.git 58 | mypackage $ git push -u origin main 59 | 60 | Where ``myusername`` and ``mypackage`` are adjusted for your username and package name. 61 | 62 | You'll need a ssh key to push the repo. You can `Generate`_ a key or `Add`_ an existing one. 63 | 64 | .. _`Generate`: https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/ 65 | .. _`Add`: https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/ 66 | 67 | Step 4: Install Dev Requirements 68 | -------------------------------- 69 | 70 | You should still be in the folder containing the ``pyproject.toml`` file. 71 | 72 | Install the new project's local development requirements inside a virtual environment using poetry: 73 | 74 | .. code-block:: bash 75 | 76 | $ poetry install 77 | $ poetry run inv install-hooks 78 | 79 | Step 5: Set Up Codecov 80 | ---------------------- 81 | 82 | `Codecov`_ provides highly integrated tools to group, merge, archive, and compare coverage reports. 83 | 84 | Log into your account at `Codecov`_. If you don't have one, create one and log into it. 85 | 86 | Click on `Add new repository`. Choose the desired one. Then follow the instructions to setup the `CODECOV_TOKEN` on the github secrets. 87 | 88 | Install the `Codecov`_ github App. 89 | 90 | Now your coverage reports will be generated when a new PR is created. 91 | 92 | .. _`Codecov`: https://codecov.io/ 93 | 94 | Step 6: Set Up Read the Docs 95 | ---------------------------- 96 | 97 | `Read the Docs`_ hosts documentation for the open source community. Think of it as Continuous Documentation. 98 | 99 | Log into your account at `Read the Docs`_ . If you don't have one, create one and log into it. 100 | 101 | If you are not at your dashboard, choose the pull-down next to your username in the upper right, and select "My Projects". Choose the button to Import the repository and follow the directions. 102 | 103 | Now your documentation will get rebuilt when you make documentation changes to your package. 104 | 105 | .. _`Read the Docs`: https://readthedocs.org/ 106 | 107 | Step 7: Release on PyPI and TestPyPI 108 | ------------------------------------ 109 | 110 | The Python Package Index or `PyPI`_ is the official third-party software repository for the Python programming language. Python developers intend it to be a comprehensive catalog of all open source Python packages. 111 | 112 | `TestPyPI`_ is a separate instance of the Python Package Index (`PyPI`_) that allows you to try out the distribution tools and process without worrying about affecting the real index. 113 | 114 | Log into your account at `PyPI`_ and `TestPyPI`_. Go to Account Settings and generate an API tokens. 115 | 116 | Go to the repository settings on GitHub, and add tow secrets named `PYPI_TOKEN` and `TEST_PYPI_TOKEN` with the tokens that you just generated. 117 | 118 | Release your package by pushing a new tag. 119 | 120 | .. _`PyPI`: https://pypi.python.org/pypi 121 | .. _`TestPyPI`: https://test.pypi.org/ 122 | 123 | Having problems? 124 | ---------------- 125 | 126 | Visit our `Issues`_ page and create a new Issue. Be sure to give as much information as possible. 127 | 128 | .. _`Issues`: https://github.com/fedejaure/cookiecutter-modern-pypackage/issues 129 | -------------------------------------------------------------------------------- /hooks/post_gen_project.py: -------------------------------------------------------------------------------- 1 | """Script that run after the project is generated.""" 2 | 3 | from pathlib import Path 4 | from typing import Union 5 | 6 | PROJECT_DIR = Path.cwd() 7 | PROJECT_TESTS = PROJECT_DIR / Path("tests") 8 | PROJECT_SRC = PROJECT_DIR / Path("src/{{ cookiecutter.project_slug }}") 9 | PROJECT_DOCS = PROJECT_DIR / Path("docs") 10 | 11 | 12 | def remove_file(filepath: Union[str, Path]) -> None: 13 | """Remove a file from the file system.""" 14 | Path.unlink(PROJECT_DIR / filepath) 15 | 16 | 17 | def add_symlink(path: Path, target: Union[str, Path], target_is_directory: bool = False) -> None: 18 | """Add symbolic link to target.""" 19 | if path.is_symlink(): 20 | path.unlink() 21 | path.symlink_to(target, target_is_directory) 22 | 23 | 24 | if __name__ == "__main__": 25 | if "No command-line interface" in "{{ cookiecutter.command_line_interface }}": 26 | remove_file(PROJECT_TESTS / "test_cli.py") 27 | remove_file(PROJECT_SRC / "cli.py") 28 | 29 | if "Not open source" == "{{ cookiecutter.open_source_license }}": 30 | remove_file("LICENSE.rst") 31 | else: 32 | add_symlink(PROJECT_DOCS / "license.rst", "../LICENSE.rst") 33 | 34 | if "{{ cookiecutter.add_code_of_conduct }}" != "y": 35 | remove_file("CODE_OF_CONDUCT.md") 36 | 37 | if "{{ cookiecutter.add_contributing_file }}" != "y": 38 | remove_file("CONTRIBUTING.md") 39 | 40 | if "{{ cookiecutter.add_security_file }}" != "y": 41 | remove_file("SECURITY.md") 42 | 43 | if "{{ cookiecutter.add_codeowners_file }}" != "y": 44 | remove_file(".github/CODEOWNERS") 45 | 46 | if "{{ cookiecutter.add_funding_file }}" != "y": 47 | remove_file(".github/FUNDING.yml") 48 | 49 | if "{{ cookiecutter.add_citation_file }}" != "y": 50 | remove_file("CITATION.cff") 51 | 52 | add_symlink(PROJECT_DOCS / "readme.md", "../README.md") 53 | add_symlink(PROJECT_DOCS / "changelog.md", "../CHANGELOG.md") 54 | -------------------------------------------------------------------------------- /hooks/pre_gen_project.py: -------------------------------------------------------------------------------- 1 | """Script that run before the project is generated.""" 2 | 3 | import re 4 | import sys 5 | 6 | NAME_REGEX = r"^[a-zA-Z][\-a-zA-Z0-9]+$" 7 | SLUG_REGEX = r"^[_a-zA-Z][_a-zA-Z0-9]+$" 8 | 9 | 10 | def validate_value(value: str, regex: str, fail_msg: str) -> None: 11 | """Validate value format.""" 12 | if not re.match(regex, value): 13 | print(fail_msg) 14 | sys.exit(1) 15 | 16 | 17 | if __name__ == "__main__": 18 | validate_value( 19 | "{{ cookiecutter.project_name }}", 20 | NAME_REGEX, 21 | ( 22 | "ERROR: The project name {value} is not a valid Github repository name. " 23 | "Please do not use a _ and use - instead" 24 | ), 25 | ) 26 | 27 | validate_value( 28 | "{{ cookiecutter.project_slug }}", 29 | SLUG_REGEX, 30 | ( 31 | "ERROR: The project slug {value} is not a valid Python module name. " 32 | "Please do not use a - and use _ instead" 33 | ), 34 | ) 35 | -------------------------------------------------------------------------------- /noxfile.py: -------------------------------------------------------------------------------- 1 | """Nox sessions.""" 2 | 3 | import nox 4 | from nox_poetry import Session, session 5 | 6 | nox.options.sessions = ["tests", "mypy"] 7 | python_versions = ["3.9", "3.10", "3.11", "3.12"] 8 | 9 | 10 | @session(python=python_versions) 11 | def tests(session: Session) -> None: 12 | """Run the test suite.""" 13 | session.install("invoke", "pytest", "xdoctest", "cookiecutter", "pytest-cookies") 14 | session.run("inv", "tests") 15 | 16 | 17 | @session(python=python_versions) 18 | def mypy(session: Session) -> None: 19 | """Type-check using mypy.""" 20 | session.install("invoke", "mypy") 21 | session.run("inv", "mypy") 22 | 23 | 24 | @session(python="3.12") 25 | def security(session: Session) -> None: 26 | """Scan dependencies for insecure packages.""" 27 | session.install("invoke", "safety") 28 | session.run("inv", "security") 29 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "cookiecutter-modern-pypackage" 3 | version = "3.1.0" 4 | description = "Cookiecutter template for a modern Python package." 5 | authors = ["Federico Jaureguialzo "] 6 | license = "MIT" 7 | readme = "README.md" 8 | homepage = "https://github.com/fedejaure/cookiecutter-modern-pypackage" 9 | repository = "https://github.com/fedejaure/cookiecutter-modern-pypackage" 10 | documentation = "https://cookiecutter-modern-pypackage.readthedocs.io" 11 | keywords=["cookiecutter", "template", "package"] 12 | classifiers=[ 13 | "Development Status :: 4 - Beta", 14 | "Environment :: Console", 15 | "Intended Audience :: Developers", 16 | "Natural Language :: English", 17 | "License :: OSI Approved :: BSD License", 18 | "Programming Language :: Python", 19 | "Programming Language :: Python :: 3", 20 | "Programming Language :: Python :: 3.9", 21 | "Programming Language :: Python :: 3.10", 22 | "Programming Language :: Python :: 3.11", 23 | "Programming Language :: Python :: 3.12", 24 | "Programming Language :: Python :: Implementation :: CPython", 25 | "Programming Language :: Python :: Implementation :: PyPy", 26 | "Topic :: Software Development", 27 | ] 28 | package-mode = false 29 | 30 | [tool.poetry.urls] 31 | "Bug Tracker" = "https://github.com/fedejaure/cookiecutter-modern-pypackage/issues" 32 | 33 | [tool.poetry.dependencies] 34 | python = "<3.13,>=3.9" 35 | cookiecutter = "^2.6.0" 36 | 37 | [tool.poetry.group.dev.dependencies] 38 | pre-commit = "^4.0.1" 39 | invoke = "^2.2.0" 40 | bump2version = "^1.0.1" 41 | watchdog = {version = "^6.0.0", extras = ["watchmedo"]} 42 | 43 | [tool.poetry.group.test.dependencies] 44 | pytest = "^8.3.3" 45 | xdoctest = "^1.2.0" 46 | pytest-cookies = "^0.7.0" 47 | 48 | [tool.poetry.group.linters.dependencies] 49 | isort = "^5.13.2" 50 | black = "^24.10.0" 51 | ruff = "^0.7.4" 52 | 53 | [tool.poetry.group.security.dependencies] 54 | safety = "^3.2.11" 55 | 56 | [tool.poetry.group.typing.dependencies] 57 | mypy = "^1.13.0" 58 | 59 | [tool.poetry.group.docs.dependencies] 60 | sphinx = "^7.4.7" 61 | recommonmark = "^0.7.1" 62 | 63 | [tool.ruff] 64 | target-version = "py39" 65 | output-format = "full" 66 | line-length = 99 67 | fix = true 68 | extend-exclude = [ 69 | "docs/*", 70 | ] 71 | 72 | [tool.ruff.lint] 73 | select = [ 74 | "E", "F", "W", # flake8 75 | "C", # mccabe 76 | "I", # isort 77 | "N", # pep8-naming 78 | "D", # flake8-docstrings 79 | "ANN", # flake8-annotations 80 | "S", # flake8-bandit 81 | "BLE", # flake8-blind-except 82 | "B", # flake8-bugbear 83 | "A", # flake8-builtins 84 | "G", # flake8-logging-format 85 | "ERA", # eradicate 86 | "ISC", # flake8-implicit-str-concat 87 | "RUF", # Ruff-specific rules 88 | ] 89 | ignore = ["ANN101"] 90 | unfixable = [ 91 | "ERA", # Don't remove commented-out code 92 | ] 93 | 94 | [tool.ruff.lint.per-file-ignores] 95 | "tests/*" = ["S101"] 96 | 97 | [tool.ruff.lint.mccabe] 98 | max-complexity = 10 99 | 100 | [tool.ruff.lint.pydocstyle] 101 | convention = "google" 102 | 103 | [tool.isort] 104 | multi_line_output = 3 105 | include_trailing_comma = true 106 | force_grid_wrap = 0 107 | use_parentheses = true 108 | line_length = 99 109 | known_third_party = ["cookiecutter", "invoke", "nox", "nox_poetry", "pytest", "pytest_cookies"] 110 | skip = "{{ cookiecutter.project_name }}" 111 | 112 | [tool.black] 113 | line-length = 99 114 | target-version = ["py39"] 115 | 116 | [tool.mypy] 117 | warn_return_any = true 118 | warn_unused_configs = true 119 | 120 | [[tool.mypy.overrides]] 121 | module = ["cookiecutter.*", "pytest.*", "pytest_cookies.*", "invoke.*", "nox.*", "nox_poetry.*"] 122 | allow_redefinition = false 123 | check_untyped_defs = true 124 | ignore_errors = false 125 | ignore_missing_imports = true 126 | implicit_reexport = true 127 | local_partial_types = true 128 | strict_optional = true 129 | strict_equality = true 130 | no_implicit_optional = true 131 | warn_unused_ignores = true 132 | warn_unreachable = true 133 | warn_no_return = true 134 | 135 | [build-system] 136 | requires = ["poetry>=0.12"] 137 | build-backend = "poetry.masonry.api" 138 | -------------------------------------------------------------------------------- /tasks.py: -------------------------------------------------------------------------------- 1 | """Tasks for maintaining the project. 2 | 3 | Execute 'invoke --list' for guidance on using Invoke 4 | """ 5 | 6 | import platform 7 | import webbrowser 8 | from pathlib import Path 9 | from typing import Optional 10 | 11 | from invoke import call, task 12 | from invoke.context import Context 13 | from invoke.runners import Result 14 | 15 | ROOT_DIR = Path(__file__).parent 16 | DOCS_DIR = ROOT_DIR.joinpath("docs") 17 | DOCS_BUILD_DIR = DOCS_DIR.joinpath("_build") 18 | DOCS_INDEX = DOCS_BUILD_DIR.joinpath("index.html") 19 | TEST_DIR = ROOT_DIR.joinpath("tests") 20 | PYTHON_TARGETS = [ 21 | TEST_DIR, 22 | ROOT_DIR.joinpath("hooks"), 23 | DOCS_DIR.joinpath("conf.py"), 24 | ROOT_DIR.joinpath("noxfile.py"), 25 | Path(__file__), 26 | ] 27 | PYTHON_TARGETS_STR = " ".join([str(p) for p in PYTHON_TARGETS]) 28 | 29 | 30 | def _run(c: Context, command: str) -> Optional[Result]: 31 | return c.run(command, pty=platform.system() != "Windows") 32 | 33 | 34 | @task() 35 | def bake(c: Context, replay: bool = False) -> None: 36 | """Bake the cookie.""" 37 | bake_options = ( 38 | ["--replay", "--overwrite-if-exists"] 39 | if replay 40 | else ["--no-input", "--overwrite-if-exists"] 41 | ) 42 | _run(c, f"poetry run cookiecutter {' '.join(bake_options)} .") 43 | 44 | 45 | @task() 46 | def watch(c: Context, replay: bool = False) -> None: 47 | """Bake and watch for changes.""" 48 | bake(c, replay=replay) 49 | _run( 50 | c, 51 | f"poetry run watchmedo shell-command -p '*.*' " 52 | f"-c 'inv bake {'--replay' if replay else ''}' " 53 | "-W -R -D {{ cookiecutter.project_name }}", 54 | ) 55 | 56 | 57 | @task() 58 | def clean_build(c: Context) -> None: 59 | """Clean up files from package building.""" 60 | _run(c, "rm -fr build/") 61 | _run(c, "rm -fr dist/") 62 | _run(c, "rm -fr .eggs/") 63 | _run(c, "find . -name '*.egg-info' -exec rm -fr {} +") 64 | _run(c, "find . -name '*.egg' -exec rm -f {} +") 65 | 66 | 67 | @task() 68 | def clean_python(c: Context) -> None: 69 | """Clean up python file artifacts.""" 70 | _run(c, "find . -name '*.pyc' -exec rm -f {} +") 71 | _run(c, "find . -name '*.pyo' -exec rm -f {} +") 72 | _run(c, "find . -name '*~' -exec rm -f {} +") 73 | _run(c, "find . -name '__pycache__' -exec rm -fr {} +") 74 | 75 | 76 | @task() 77 | def clean_tests(c: Context) -> None: 78 | """Clean up files from testing.""" 79 | _run(c, "rm -fr .pytest_cache") 80 | 81 | 82 | @task() 83 | def clean_docs(c: Context) -> None: 84 | """Clean up files from documentation builds.""" 85 | _run(c, f"rm -fr {DOCS_BUILD_DIR}") 86 | 87 | 88 | @task(pre=[clean_build, clean_python, clean_tests, clean_docs]) 89 | def clean(c: Context) -> None: 90 | """Run all clean sub-tasks.""" 91 | 92 | 93 | @task() 94 | def install_hooks(c: Context) -> None: 95 | """Install pre-commit hooks.""" 96 | _run(c, "poetry run pre-commit install") 97 | 98 | 99 | @task() 100 | def hooks(c: Context) -> None: 101 | """Run pre-commit hooks.""" 102 | _run(c, "poetry run pre-commit run --all-files") 103 | 104 | 105 | @task(name="format", help={"check": "Checks if source is formatted without applying changes"}) 106 | def format_(c: Context, check: bool = False) -> None: 107 | """Format code.""" 108 | isort_options = ["--check-only", "--diff"] if check else [] 109 | _run(c, f"poetry run isort {' '.join(isort_options)} {PYTHON_TARGETS_STR}") 110 | black_options = ["--diff", "--check"] if check else ["--quiet"] 111 | _run(c, f"poetry run black {' '.join(black_options)} {PYTHON_TARGETS_STR}") 112 | 113 | 114 | @task() 115 | def ruff(c: Context) -> None: 116 | """Run ruff.""" 117 | _run(c, f"poetry run ruff check {PYTHON_TARGETS_STR}") 118 | 119 | 120 | @task() 121 | def security(c: Context) -> None: 122 | """Run security related checks.""" 123 | _run( 124 | c, 125 | "poetry export --with dev --format=requirements.txt --without-hashes | " 126 | "poetry run safety check --stdin --full-report", 127 | ) 128 | 129 | 130 | @task(pre=[ruff, security, call(format_, check=True)]) 131 | def lint(c: Context) -> None: 132 | """Run all linting.""" 133 | 134 | 135 | @task() 136 | def mypy(c: Context) -> None: 137 | """Run mypy.""" 138 | _run(c, f"poetry run mypy {PYTHON_TARGETS_STR}") 139 | 140 | 141 | @task() 142 | def tests(c: Context) -> None: 143 | """Run tests.""" 144 | pytest_options = ["--xdoctest"] 145 | _run(c, f"poetry run pytest {' '.join(pytest_options)} {TEST_DIR}") 146 | 147 | 148 | @task( 149 | help={ 150 | "serve": "Build the docs watching for changes", 151 | "open_browser": "Open the docs in the web browser", 152 | } 153 | ) 154 | def docs(c: Context, serve: bool = False, open_browser: bool = False) -> None: 155 | """Build documentation.""" 156 | build_docs = f"sphinx-build -b html {DOCS_DIR} {DOCS_BUILD_DIR}" 157 | _run(c, build_docs) 158 | if open_browser: 159 | webbrowser.open(DOCS_INDEX.absolute().as_uri()) 160 | if serve: 161 | _run(c, f"poetry run watchmedo shell-command -p '*.rst;*.md' -c '{build_docs}' -R -D .") 162 | 163 | 164 | @task( 165 | help={ 166 | "part": "Part of the version to be bumped.", 167 | "dry_run": "Don't write any files, just pretend. (default: False)", 168 | } 169 | ) 170 | def version(c: Context, part: str, dry_run: bool = False) -> None: 171 | """Bump version.""" 172 | bump_options = ["--dry-run"] if dry_run else [] 173 | _run(c, f"poetry run bump2version {' '.join(bump_options)} {part}") 174 | -------------------------------------------------------------------------------- /tests/test_bake_project.py: -------------------------------------------------------------------------------- 1 | """Tests for `cookiecutter-modern-pypackage` package.""" 2 | 3 | import datetime 4 | import os 5 | import shlex 6 | import subprocess 7 | import sys 8 | from contextlib import contextmanager 9 | from typing import Dict, Generator, List, Optional 10 | 11 | import pytest 12 | from cookiecutter.utils import rmtree 13 | from pytest_cookies.plugin import Cookies, Result 14 | 15 | skip_on_windows = pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") 16 | 17 | COOKIE_CONTEXT_NOT_OPEN_SOURCE = {"open_source_license": "Not open source"} 18 | COOKIE_CONTEXT_CLI = {"command_line_interface": "Typer"} 19 | COOKIE_CONTEXT_BSD = {"open_source_license": "BSD"} 20 | COOKIE_CONTEXT_NO_CLI = {"command_line_interface": "No command-line interface"} 21 | COOKIE_CONTEXT_DIFF_NAME_AND_SLUG = { 22 | "project_name": "some-project-name", 23 | "project_slug": "some_project", 24 | } 25 | 26 | 27 | @contextmanager 28 | def inside_dir(dirpath: str) -> Generator[None, None, None]: 29 | """Execute code from inside the given directory. 30 | 31 | Args: 32 | dirpath: A path to the directory where the command is being run. 33 | 34 | Yields: 35 | None. 36 | """ 37 | old_path = os.getcwd() 38 | try: 39 | os.chdir(dirpath) 40 | yield 41 | finally: 42 | os.chdir(old_path) 43 | 44 | 45 | @contextmanager 46 | def bake_in_temp_dir(cookies: Cookies, extra_context: Optional[Dict[str, str]] = None) -> Result: 47 | """Bake a cookie and clean up the temporary directory created during tests. 48 | 49 | Args: 50 | cookies: The cookie to be baked, and its temporal files will be removed. 51 | extra_context: An optional additional context to be provided to the bake command. 52 | 53 | Yields: 54 | The baked cookie. 55 | """ 56 | result = cookies.bake(extra_context=extra_context) 57 | try: 58 | yield result 59 | finally: 60 | rmtree(str(result.project_path)) 61 | 62 | 63 | def run_inside_dir(command: str, dirpath: str) -> int: 64 | """Run a command from inside a given directory, returning the exit status. 65 | 66 | Args: 67 | command: A command that will be executed. 68 | dirpath: A path of the directory the command is being run. 69 | 70 | Returns: 71 | The return code of the command. 72 | """ 73 | with inside_dir(dirpath): 74 | return subprocess.check_call(shlex.split(command), shell=False) # noqa: S603 75 | 76 | 77 | def test_year_compute_in_license_file(cookies: Cookies) -> None: 78 | """Test that the year computed is in the license file.""" 79 | with bake_in_temp_dir(cookies) as result: 80 | license_file_path = result.project_path.joinpath("LICENSE.rst") 81 | now = datetime.datetime.now() 82 | assert str(now.year) in license_file_path.read_text() 83 | 84 | 85 | def test_bake_with_defaults(cookies: Cookies) -> None: 86 | """Test bake the project with the default values.""" 87 | with bake_in_temp_dir(cookies) as result: 88 | assert result.project_path.is_dir() 89 | assert result.exit_code == 0 90 | assert result.exception is None 91 | 92 | found_toplevel_files = [f.name for f in result.project_path.iterdir()] 93 | assert "src" in found_toplevel_files 94 | assert "tests" in found_toplevel_files 95 | 96 | 97 | def test_bake_not_open_source(cookies: Cookies) -> None: 98 | """Test bake not open-source project.""" 99 | with bake_in_temp_dir(cookies, extra_context=COOKIE_CONTEXT_NOT_OPEN_SOURCE) as result: 100 | assert result.project_path.is_dir() 101 | assert result.exit_code == 0 102 | assert result.exception is None 103 | 104 | found_toplevel_files = [f.name for f in result.project_path.iterdir()] 105 | assert "LICENSE.rst" not in found_toplevel_files 106 | assert "License" not in result.project_path.joinpath("README.md").read_text() 107 | 108 | 109 | def _test_bake_and_run_invoke_tasks( 110 | cookies: Cookies, extra_context: Dict[str, str], inv_tasks: List[str] 111 | ) -> None: 112 | """Test bake the project and check invoke tasks.""" 113 | with bake_in_temp_dir(cookies, extra_context=extra_context) as result: 114 | assert result.project_path.is_dir() 115 | assert result.exit_code == 0 116 | assert result.exception is None 117 | 118 | assert run_inside_dir("git init", str(result.project_path)) == 0 119 | assert run_inside_dir("git add .", str(result.project_path)) == 0 120 | assert run_inside_dir('git config user.email "t@test.com"', str(result.project_path)) == 0 121 | assert run_inside_dir('git config user.name "Test User"', str(result.project_path)) == 0 122 | assert run_inside_dir("git commit -m 'initial commit'", str(result.project_path)) == 0 123 | assert run_inside_dir("poetry install", str(result.project_path)) == 0 124 | for task in inv_tasks: 125 | assert run_inside_dir(f"poetry run inv {task}", str(result.project_path)) == 0 126 | 127 | 128 | @pytest.mark.parametrize( 129 | "extra_context", 130 | [ 131 | None, 132 | COOKIE_CONTEXT_NOT_OPEN_SOURCE, 133 | {**COOKIE_CONTEXT_NOT_OPEN_SOURCE, **COOKIE_CONTEXT_NO_CLI}, 134 | {**COOKIE_CONTEXT_CLI, **COOKIE_CONTEXT_BSD}, 135 | COOKIE_CONTEXT_DIFF_NAME_AND_SLUG, 136 | ], 137 | ) 138 | def test_bake_and_run_invoke(cookies: Cookies, extra_context: Dict[str, str]) -> None: 139 | """Test bake the project and check invoke tasks.""" 140 | invoke_tasks = [ 141 | "install-hooks", 142 | "hooks", 143 | "clean", 144 | "lint", 145 | "mypy", 146 | "tests", 147 | "coverage", 148 | "version -d minor", 149 | "docs", 150 | ] 151 | _test_bake_and_run_invoke_tasks(cookies, extra_context, invoke_tasks) 152 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | commit = True 3 | tag = False 4 | current_version = {{ cookiecutter.version }} 5 | 6 | [bumpversion:file:pyproject.toml] 7 | search = version = "{current_version}" 8 | replace = version = "{new_version}" 9 | 10 | [bumpversion:file:src/{{cookiecutter.project_slug}}/__init__.py] 11 | search = __version__ = "{current_version}" 12 | replace = __version__ = "{new_version}" 13 | {% raw %} 14 | [bumpversion:file(title):CHANGELOG.md] 15 | search = {#}{#} [Unreleased] 16 | replace = {#}{#} [Unreleased] 17 | 18 | {#}{#} [{new_version}] - {now:%Y-%m-%d} 19 | {% endraw %} 20 | [bumpversion:file(links):CHANGELOG.md] 21 | search = [Unreleased]: https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}/compare/v{current_version}...HEAD 22 | replace = [Unreleased]: https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}/compare/v{new_version}...HEAD 23 | [{new_version}]: https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}/compare/v{current_version}...v{new_version} 24 | {% if cookiecutter.add_citation_file == 'y' %} 25 | [bumpversion:file(version):CITATION.cff] 26 | search = version: {current_version} 27 | replace = version: {new_version} 28 | 29 | [bumpversion:file(tag):CITATION.cff] 30 | search = https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}/releases/tag/v{current_version} 31 | replace = https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}/releases/tag/v{new_version} 32 | 33 | [bumpversion:file(description):CITATION.cff] 34 | search = description: The Software Heritage link for version {current_version}. 35 | replace = description: The Software Heritage link for version {new_version}. 36 | {% endif -%} 37 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This is a comment. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # More details are here: https://help.github.com/articles/about-codeowners/ 5 | 6 | # These owners will be the default owners for everything in 7 | # the repo. Unless a later match takes precedence, 8 | # @{{ cookiecutter.github_username }} will be requested for review when someone opens 9 | # a pull request. 10 | * @{{ cookiecutter.github_username }} 11 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This is a comment. 3 | # You can configure the sponsor button in your repository to include 4 | # sponsored developers in GitHub Sponsors, external funding platforms, 5 | # or a custom funding URL. 6 | 7 | # More details are here: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository 8 | github: {{ cookiecutter.github_username }} 9 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug report 3 | about: Create a report to help us improve 4 | labels: bug 5 | assignees: '' 6 | 7 | --- 8 | 9 | ## Expected Behavior 10 | 11 | 12 | ## Actual Behavior 13 | 14 | 15 | ## Steps to Reproduce the Problem 16 | 17 | 1. 18 | 1. 19 | 1. 20 | 21 | ## Specifications 22 | 23 | - Version: 24 | - Platform: 25 | - Subsystem: 26 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: [] 3 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature request 3 | about: Suggest an idea for this project 4 | labels: enhancement 5 | assignees: '' 6 | 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # 2 | 3 | ## Proposed Changes 4 | 5 | - 6 | - 7 | - 8 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: pip 8 | directory: "/docs" 9 | schedule: 10 | interval: daily 11 | - package-ecosystem: pip 12 | directory: "/" 13 | schedule: 14 | interval: daily 15 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | {% raw %}name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: 6 | pull_request: 7 | branches: 8 | schedule: 9 | - cron: '0 6 * * 1' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | language: [ 'python' ] 24 | 25 | steps: 26 | - name: Checkout repository 27 | uses: actions/checkout@v4.2.2 28 | 29 | # Initializes the CodeQL tools for scanning. 30 | - name: Initialize CodeQL 31 | uses: github/codeql-action/init@v3 32 | with: 33 | languages: ${{ matrix.language }} 34 | 35 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 36 | # If this step fails, then you should remove it and run the build manually (see below) 37 | - name: Autobuild 38 | uses: github/codeql-action/autobuild@v3 39 | 40 | - name: Perform CodeQL Analysis 41 | uses: github/codeql-action/analyze@v3{% endraw %} 42 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.github/workflows/pre-commit-autoupdate.yml: -------------------------------------------------------------------------------- 1 | {% raw %}name: "Pre-commit autoupdate" 2 | 3 | on: 4 | schedule: 5 | - cron: '0 6 * * 1' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | autoupdate: 10 | name: autoupdate 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4.2.2 14 | 15 | - name: Set up Python 3.9 16 | uses: actions/setup-python@v5.3.0 17 | with: 18 | python-version: 3.9 19 | 20 | - name: Install system deps 21 | shell: bash 22 | run: | 23 | pip install poetry 24 | poetry config virtualenvs.in-project true 25 | poetry install --no-root --only dev --only linters --sync 26 | 27 | - name: Run autoupdate 28 | run: poetry run pre-commit autoupdate 29 | continue-on-error: true 30 | 31 | - name: Run pre-commit 32 | run: poetry run pre-commit run --all-files 33 | 34 | - uses: peter-evans/create-pull-request@v7.0.5 35 | with: 36 | token: ${{ secrets.GITHUB_TOKEN }} 37 | branch: chore-update-pre-commit-hooks 38 | title: Update pre-commit hooks 39 | commit-message: "Update pre-commit hooks" 40 | body: | 41 | # Update pre-commit hooks 42 | 43 | - Update pre-commit hooks to the latest version. 44 | delete-branch: true{% endraw %} 45 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | {%- set is_open_source = cookiecutter.open_source_license != 'Not open source' -%} 2 | name: release 3 | 4 | on: 5 | push: 6 | tags: 7 | - 'v*' 8 | 9 | permissions: 10 | id-token: write 11 | contents: write 12 | 13 | jobs: 14 | release: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4.2.2 18 | 19 | - name: Set up Python 3.9 20 | uses: actions/setup-python@v5.3.0 21 | with: 22 | python-version: 3.9 23 | 24 | - name: Install system deps 25 | shell: bash 26 | run: | 27 | pip install poetry 28 | poetry config virtualenvs.in-project true 29 | 30 | - name: Build package 31 | run: | 32 | poetry build --ansi 33 | 34 | {%- if is_open_source %} 35 | 36 | {% raw -%} 37 | - name: Publish package on PyPI 38 | uses: pypa/gh-action-pypi-publish@v1.8.11 39 | with: 40 | user: __token__ 41 | password: ${{ secrets.PYPI_TOKEN }} 42 | attestations: false 43 | 44 | - name: Publish package on TestPyPI 45 | uses: pypa/gh-action-pypi-publish@v1.8.11 46 | with: 47 | user: __token__ 48 | password: ${{ secrets.TEST_PYPI_TOKEN }} 49 | repository-url: https://test.pypi.org/legacy/ 50 | attestations: false 51 | {%- endraw %} 52 | 53 | {%- endif %} 54 | 55 | {% raw -%} 56 | github_release: 57 | needs: release 58 | name: Create Github Release 59 | runs-on: ubuntu-latest 60 | steps: 61 | - uses: actions/checkout@v4.2.2 62 | 63 | - name: Get version from tag 64 | id: tag_name 65 | shell: bash 66 | run: | 67 | echo ::set-output name=current_version::${GITHUB_REF#refs/tags/v} 68 | 69 | - name: Get Changelog Entry 70 | id: changelog_reader 71 | uses: mindsers/changelog-reader-action@v2.2.3 72 | with: 73 | version: ${{ steps.tag_name.outputs.current_version }} 74 | path: ./CHANGELOG.md 75 | 76 | - name: Create Release 77 | id: create_release 78 | uses: actions/create-release@v1.1.4 79 | env: 80 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 81 | with: 82 | tag_name: ${{ github.ref }} 83 | release_name: Release ${{ github.ref }} 84 | body: ${{ steps.changelog_reader.outputs.changes }} 85 | draft: false 86 | prerelease: false 87 | {%- endraw %} 88 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | {% raw %}name: tests 2 | 3 | on: 4 | push: 5 | branches: 6 | pull_request: 7 | branches: 8 | 9 | jobs: 10 | linting: 11 | name: Linting 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4.2.2 15 | 16 | - name: Set up Python 3.9 17 | uses: actions/setup-python@v5.3.0 18 | with: 19 | python-version: 3.9 20 | 21 | - name: Install system deps 22 | shell: bash 23 | run: | 24 | pip install poetry 25 | poetry config virtualenvs.in-project true 26 | poetry install --no-root --only dev --only linters --sync 27 | 28 | - name: Linting 29 | shell: bash 30 | run: poetry run pre-commit run --all-files 31 | 32 | tests: 33 | needs: linting 34 | name: ${{ matrix.os }} / ${{ matrix.python-version }} 35 | runs-on: ${{ matrix.os }}-latest 36 | strategy: 37 | matrix: 38 | os: [Ubuntu, MacOS, Windows] 39 | python-version: ['3.9', '3.10', '3.11', '3.12'] 40 | fail-fast: true 41 | steps: 42 | - uses: actions/checkout@v4.2.2 43 | 44 | - name: Set up Python ${{ matrix.python-version }} 45 | uses: actions/setup-python@v5.3.0 46 | with: 47 | python-version: ${{ matrix.python-version }} 48 | 49 | - name: Install system deps 50 | shell: bash 51 | run: | 52 | pip install nox-poetry 53 | pip install poetry 54 | poetry config virtualenvs.in-project true 55 | 56 | - name: Run mypy with nox 57 | shell: bash 58 | run: nox --force-color -s mypy-${{ matrix.python-version }} 59 | 60 | - name: Run tests with nox 61 | shell: bash 62 | run: nox --force-color -s tests-${{ matrix.python-version }} 63 | 64 | - name: Run securtity check 65 | if: matrix.python-version == '3.12' && matrix.os == 'Ubuntu' 66 | shell: bash 67 | run: nox --force-color -s security 68 | 69 | - name: Upload coverage data 70 | uses: actions/upload-artifact@v4.4.3 71 | with: 72 | name: ${{ matrix.os }}-${{ matrix.python-version }}.coverage-data 73 | path: ".coverage.*" 74 | include-hidden-files: true 75 | retention-days: 2 76 | 77 | coverage: 78 | needs: tests 79 | runs-on: ubuntu-latest 80 | steps: 81 | - uses: actions/checkout@v4.2.2 82 | 83 | - name: Set up Python 3.9 84 | uses: actions/setup-python@v5.3.0 85 | with: 86 | python-version: 3.9 87 | 88 | - name: Install system deps 89 | shell: bash 90 | run: | 91 | pip install nox-poetry 92 | pip install poetry 93 | poetry config virtualenvs.in-project true 94 | 95 | - name: Download coverage data 96 | uses: actions/download-artifact@v4.1.8 97 | with: 98 | pattern: "*.coverage-data" 99 | merge-multiple: true 100 | 101 | - name: Create coverage report 102 | shell: bash 103 | run: | 104 | nox --force-color --session=coverage -- --fmt xml 105 | 106 | - name: Upload coverage report 107 | uses: codecov/codecov-action@v4.4.1 108 | with: 109 | token: ${{ secrets.CODECOV_TOKEN }}{% endraw %} 110 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.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 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # pytype 129 | .pytype/ 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # Code editors 135 | .vscode 136 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | default_install_hook_types: 3 | - pre-commit 4 | - post-checkout 5 | - post-merge 6 | repos: 7 | - repo: https://github.com/pre-commit/pre-commit-hooks 8 | rev: v5.0.0 9 | hooks: 10 | - id: check-toml 11 | - id: check-json 12 | - id: check-yaml 13 | - id: debug-statements 14 | - id: check-merge-conflict 15 | - id: pretty-format-json 16 | args: [--autofix, '--no-sort-keys'] 17 | exclude: .ipynb 18 | - id: end-of-file-fixer 19 | - id: trailing-whitespace 20 | exclude: .bumpversion.cfg 21 | - repo: https://github.com/timothycrosley/isort 22 | rev: 5.13.2 23 | hooks: 24 | - id: isort 25 | - repo: https://github.com/psf/black 26 | rev: 24.10.0 27 | hooks: 28 | - id: black 29 | - repo: https://github.com/astral-sh/ruff-pre-commit 30 | rev: v0.7.4 31 | hooks: 32 | - id: ruff 33 | args: [ --fix ] 34 | - repo: https://github.com/citation-file-format/cffconvert 35 | rev: 5295f87c0e261da61a7b919fc754e3a77edd98a7 36 | hooks: 37 | - id: validate-cff 38 | - repo: https://github.com/python-poetry/poetry 39 | rev: 1.8.3 40 | hooks: 41 | - id: poetry-check 42 | - id: poetry-install 43 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.9" 7 | 8 | formats: all 9 | 10 | sphinx: 11 | configuration: docs/conf.py 12 | 13 | python: 14 | install: 15 | - requirements: docs/requirements.txt 16 | - method: pip 17 | path: . 18 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/.safety-policy.yml: -------------------------------------------------------------------------------- 1 | version: '2.0' 2 | 3 | # Safety Security and License Configuration file 4 | security: # configuration for the `safety check` command 5 | ignore-cvss-severity-below: 0 # A severity number between 0 and 10. Some helpful reference points: 9=ignore all vulnerabilities except CRITICAL severity. 7=ignore all vulnerabilities except CRITICAL 6 | ignore-cvss-unknown-severity: False # True or False. We recommend you set this to False. 7 | ignore-vulnerabilities: # Here you can list multiple specific vulnerabilities you want to ignore (optionally for a time period) 8 | # We recommend making use of the optional `reason` and `expires` keys for each vulnerability that you ignore. 9 | # 70612: 10 | # reason: we do not use the vulnerable function 11 | # expires: '2025-10-10' 12 | continue-on-vulnerability-error: False # Suppress non-zero exit codes when vulnerabilities are found. Enable this in pipelines and CI/CD processes if you want to pass builds that have vulnerabilities 13 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | 8 | ## [Unreleased] 9 | 10 | ## [{{ cookiecutter.version }}] - {% now 'local' %} 11 | ### Added 12 | - First release on PyPI. 13 | 14 | [Unreleased]: https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}/compare/v{{ cookiecutter.version }}...HEAD 15 | [{{ cookiecutter.version }}]: https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}/compare/releases/tag/v{{ cookiecutter.version }} 16 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/CITATION.cff: -------------------------------------------------------------------------------- 1 | # This CITATION.cff file was generated with cffinit. 2 | # Visit https://bit.ly/cffinit to generate yours today! 3 | 4 | cff-version: 1.2.0 5 | title: {{ cookiecutter.project_title }} 6 | message: >- 7 | If you use this software, please cite it using the 8 | metadata from this file. 9 | type: software 10 | authors: 11 | - given-names: {{ cookiecutter.given_names }} 12 | family-names: {{ cookiecutter.family_names }} 13 | alias: fedejaure 14 | identifiers: 15 | - type: url 16 | value: >- 17 | https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}/releases/tag/v{{ cookiecutter.version }} 18 | description: The Software Heritage link for version {{ cookiecutter.version }}. 19 | repository-code: 'https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}' 20 | abstract: {{ cookiecutter.project_short_description }} 21 | keywords: 22 | - {{ cookiecutter.project_name }} 23 | license: MIT 24 | version: {{ cookiecutter.version }} 25 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official email address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | {{ cookiecutter.contact_method|default("[INSERT CONTACT METHOD]", true) }}. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series of 87 | actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or permanent 94 | ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within the 114 | community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 127 | [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to {{ cookiecutter.project_title }} 2 | 3 | 👏🎉 First off all, Thanks for your interest in contributing to our project! 🎉👏 4 | 5 | The following is a set of guidelines for contributing to {{ cookiecutter.project_title }}. These are 6 | mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. 7 | 8 | {% if cookiecutter.add_code_of_conduct == 'y' %}## Code of Conduct 9 | 10 | We take our open source community seriously and hold ourselves and other contributors to high standards of communication. By participating and contributing to this project, you agree to uphold our [Code of Conduct](CODE_OF_CONDUCT.md).{% endif %} 11 | 12 | ## Getting Started 13 | 14 | ### Requirements 15 | 16 | We use `poetry` to manage and install dependencies. [Poetry](https://python-poetry.org/) provides a custom installer that will install `poetry` isolated from the rest of your system. 17 | 18 | ``` 19 | curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | python - 20 | ``` 21 | 22 | We'll also need `nox` for automated testing in multiple Python environments so [install that too](https://nox.thea.codes/en/stable/). 23 | 24 | To install the local development requirements inside a virtual environment run: 25 | 26 | ``` 27 | $ poetry install 28 | $ poetry run inv install-hooks 29 | ``` 30 | 31 | > For more information about `poetry` check the [docs](https://python-poetry.org/docs/). 32 | 33 | We use [invoke](http://www.pyinvoke.org/) to wrap up some useful tasks like formatting, linting, testing and more. 34 | 35 | Execute `inv[oke] --list` to see the list of available commands. 36 | 37 | ## Contributing 38 | 39 | ### Issues 40 | 41 | We use GitHub issues to track public bugs/enhancements. Report a new one by [opening a new issue](https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}/issues). 42 | 43 | In this repository, we provide a couple of templates for you to fill in for: 44 | 45 | * Bugs 46 | * Feature Requests/Enhancements 47 | 48 | Please read each section in the templates and provide as much information as you can. Please do not put any sensitive information, 49 | such as personally identifiable information, connection strings or cloud credentials. The more information you can provide, the better we can help you. 50 | 51 | ### Pull Requests 52 | 53 | Please follow these steps to have your contribution considered by the maintainers: 54 | 55 | 1. Fork the repo and create your branch locally with a succinct but descriptive name. 56 | 2. Add tests for the new changes 57 | 3. Edit documentation if you have changed something significant 58 | 4. Make sure to follow the [styleguides](#styleguides) 59 | 5. Open a PR in our repository and follow the PR template so that we can efficiently review the changes 60 | 6. After you submit your pull request, verify that all status checks are passing 61 | 62 | While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design 63 | work, tests, or other changes before your pull request can be ultimately accepted. 64 | 65 | ## Styleguides 66 | 67 | ### Python Code Style 68 | 69 | All Python code is linted with [Ruff](https://github.com/astral-sh/ruff) and formated with 70 | [Isort](https://github.com/PyCQA/isort) and [Black](https://github.com/psf/black). You can 71 | execute `inv[oke] lint` and `inv[oke] format`. 72 | 73 | ## Additional Notes 74 | 75 | If you have any question feel free to contact us at {{ cookiecutter.contact_method|default("[INSERT CONTACT METHOD]", true) }}. 76 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/LICENSE.rst: -------------------------------------------------------------------------------- 1 | {%- if cookiecutter.open_source_license == 'MIT' %} 2 | MIT License 3 | =========== 4 | 5 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }} 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | {%- elif cookiecutter.open_source_license == 'BSD' %} 25 | BSD License 26 | =========== 27 | 28 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }} 29 | All rights reserved. 30 | 31 | Redistribution and use in source and binary forms, with or without modification, 32 | are permitted provided that the following conditions are met: 33 | 34 | * Redistributions of source code must retain the above copyright notice, this 35 | list of conditions and the following disclaimer. 36 | 37 | * Redistributions in binary form must reproduce the above copyright notice, this 38 | list of conditions and the following disclaimer in the documentation and/or 39 | other materials provided with the distribution. 40 | 41 | * Neither the name of the copyright holder nor the names of its 42 | contributors may be used to endorse or promote products derived from this 43 | software without specific prior written permission. 44 | 45 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 46 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 47 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 48 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 49 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 50 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 51 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 52 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 53 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 54 | OF THE POSSIBILITY OF SUCH DAMAGE. 55 | {%- elif cookiecutter.open_source_license == 'ISC' %} 56 | ISC License 57 | =========== 58 | 59 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }} 60 | 61 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 62 | 63 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 64 | {% elif cookiecutter.open_source_license == 'Apache Software License 2.0' -%} 65 | Apache Software License 2.0 66 | 67 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }} 68 | 69 | Licensed under the Apache License, Version 2.0 (the "License"); 70 | you may not use this file except in compliance with the License. 71 | You may obtain a copy of the License at 72 | 73 | http://www.apache.org/licenses/LICENSE-2.0 74 | 75 | Unless required by applicable law or agreed to in writing, software 76 | distributed under the License is distributed on an "AS IS" BASIS, 77 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 78 | See the License for the specific language governing permissions and 79 | limitations under the License. 80 | {%- elif cookiecutter.open_source_license == 'GNU General Public License v3' %} 81 | GNU GENERAL PUBLIC LICENSE 82 | Version 3, 29 June 2007 83 | 84 | {{ cookiecutter.project_short_description }} 85 | Copyright (C) {% now 'local', '%Y' %} {{ cookiecutter.full_name }} 86 | 87 | This program is free software: you can redistribute it and/or modify 88 | it under the terms of the GNU General Public License as published by 89 | the Free Software Foundation, either version 3 of the License, or 90 | (at your option) any later version. 91 | 92 | This program is distributed in the hope that it will be useful, 93 | but WITHOUT ANY WARRANTY; without even the implied warranty of 94 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 95 | GNU General Public License for more details. 96 | 97 | You should have received a copy of the GNU General Public License 98 | along with this program. If not, see . 99 | 100 | Also add information on how to contact you by electronic and paper mail. 101 | 102 | You should also get your employer (if you work as a programmer) or school, 103 | if any, to sign a "copyright disclaimer" for the program, if necessary. 104 | For more information on this, and how to apply and follow the GNU GPL, see 105 | . 106 | 107 | The GNU General Public License does not permit incorporating your program 108 | into proprietary programs. If your program is a subroutine library, you 109 | may consider it more useful to permit linking proprietary applications with 110 | the library. If this is what you want to do, use the GNU Lesser General 111 | Public License instead of this License. But first, please read 112 | . 113 | {%- endif %} 114 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/README.md: -------------------------------------------------------------------------------- 1 | {% set is_open_source = cookiecutter.open_source_license != 'Not open source' %} 2 | # {{ cookiecutter.project_title }} 3 | 4 | {% if is_open_source %} 5 |
6 | 7 | [![PyPI - Version](https://img.shields.io/pypi/v/{{ cookiecutter.project_name }}.svg)](https://pypi.python.org/pypi/{{ cookiecutter.project_name }}) 8 | [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{{ cookiecutter.project_name }}.svg)](https://pypi.python.org/pypi/{{ cookiecutter.project_name }}) 9 | [![Tests](https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}/workflows/tests/badge.svg)](https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}/actions?workflow=tests) 10 | [![Codecov](https://codecov.io/gh/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}/branch/main/graph/badge.svg)](https://codecov.io/gh/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}) 11 | [![Read the Docs](https://readthedocs.org/projects/{{ cookiecutter.project_name }}/badge/)](https://{{ cookiecutter.project_name }}.readthedocs.io/) 12 | [![PyPI - License](https://img.shields.io/pypi/l/{{ cookiecutter.project_name }}.svg)](https://pypi.python.org/pypi/{{ cookiecutter.project_name }}) 13 | 14 | [![Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) 15 | [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) 16 | {% if cookiecutter.add_code_of_conduct == 'y' %}[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](https://www.contributor-covenant.org/version/2/1/code_of_conduct/){% endif %} 17 | 18 |
19 | {% endif %} 20 | 21 | {{ cookiecutter.project_short_description }} 22 | 23 | {% if is_open_source %} 24 | * GitHub repo: 25 | * Documentation: 26 | * Free software: {{ cookiecutter.open_source_license }} 27 | {% endif %} 28 | 29 | ## Features 30 | 31 | * TODO 32 | 33 | ## Quickstart 34 | 35 | TODO 36 | 37 | ## Credits 38 | 39 | This package was created with [Cookiecutter][cookiecutter] and the [fedejaure/cookiecutter-modern-pypackage][cookiecutter-modern-pypackage] project template. 40 | 41 | [cookiecutter]: https://github.com/cookiecutter/cookiecutter 42 | [cookiecutter-modern-pypackage]: https://github.com/fedejaure/cookiecutter-modern-pypackage 43 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | We takes the security of our software products seriously. 4 | 5 | If you believe you have found a security vulnerability, please report it to us as described below. 6 | 7 | ## Reporting Security Issues 8 | 9 | **Please do not report security vulnerabilities through public GitHub issues.** 10 | 11 | Report security vulnerabilities by emailing the lead maintainer at {{ cookiecutter.contact_method|default("[INSERT CONTACT METHOD]", true) }}. 12 | 13 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. 14 | 15 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 16 | 17 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 18 | * Full paths of source file(s) related to the manifestation of the issue 19 | * The location of the affected source code (tag/branch/commit or direct URL) 20 | * Any special configuration required to reproduce the issue 21 | * Step-by-step instructions to reproduce the issue 22 | * Proof-of-concept or exploit code (if possible) 23 | * Impact of the issue, including how an attacker might exploit the issue 24 | 25 | This information will help us triage your report more quickly. 26 | 27 | ## Preferred Languages 28 | 29 | We prefer all communications to be in English. 30 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: "100" 6 | patch: 7 | default: 8 | target: "100" 9 | comment: 10 | require_changes: true 11 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | .logo a { overflow-wrap: normal } 2 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/docs/conf.py: -------------------------------------------------------------------------------- 1 | """Sphinx configuration.""" 2 | 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file only contains a selection of the most common options. For a full 6 | # list see the documentation: 7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 8 | 9 | 10 | # If extensions (or modules to document with autodoc) are in another 11 | # directory, add these directories to sys.path here. If the directory is 12 | # relative to the documentation root, use os.path.abspath to make it 13 | # absolute, like shown here. 14 | import {{ cookiecutter.project_slug }} 15 | 16 | # -- Project information ----------------------------------------------------- 17 | 18 | # General information about the project. 19 | project = "{{ cookiecutter.project_name }}" 20 | copyright = "{% now 'local', '%Y' %}, {{ cookiecutter.full_name }}" # noqa: A001 21 | author = "{{ cookiecutter.full_name }}" 22 | # The version info for the project you're documenting, acts as replacement 23 | # for |version| and |release|, also used in various other places throughout 24 | # the built documents. 25 | # 26 | # The short X.Y version. 27 | version = {{ cookiecutter.project_slug }}.__version__ 28 | # The full version, including alpha/beta/rc tags. 29 | release = {{ cookiecutter.project_slug }}.__version__ 30 | 31 | # -- General configuration --------------------------------------------------- 32 | 33 | # Add any Sphinx extension module names here, as strings. They can be 34 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 35 | # ones. 36 | extensions = [ 37 | "sphinx.ext.autodoc", 38 | "sphinx.ext.viewcode", 39 | "sphinx.ext.napoleon", 40 | "recommonmark", 41 | ] 42 | 43 | # Add any paths that contain templates here, relative to this directory. 44 | templates_path = ["_templates"] 45 | 46 | # List of patterns, relative to source directory, that match files and 47 | # directories to ignore when looking for source files. 48 | # This pattern also affects html_static_path and html_extra_path. 49 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 50 | 51 | 52 | # -- Options for HTML output ------------------------------------------------- 53 | 54 | # The theme to use for HTML and HTML Help pages. See the documentation for 55 | # a list of builtin themes. 56 | # 57 | html_theme = "alabaster" 58 | 59 | # Theme options are theme-specific and customize the look and feel of a theme 60 | # further. For a list of options available for each theme, see the 61 | # documentation. 62 | html_theme_options = { 63 | "github_user": "{{ cookiecutter.github_username }}", 64 | "github_repo": "{{ cookiecutter.project_name }}", 65 | "github_banner": True, 66 | "show_related": False, 67 | "fixed_sidebar": True, 68 | } 69 | 70 | # Add any paths that contain custom static files (such as style sheets) here, 71 | # relative to this directory. They are copied after the builtin static files, 72 | # so a file named "default.css" will overwrite the builtin "default.css". 73 | html_static_path = ["_static"] 74 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to {{ cookiecutter.project_title }}'s documentation! 2 | =========================================================== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | readme 8 | installation 9 | usage 10 | modules 11 | changelog 12 | 13 | Indices and tables 14 | ================== 15 | * :ref:`genindex` 16 | * :ref:`modindex` 17 | * :ref:`search` 18 | 19 | {%- if cookiecutter.open_source_license != 'Not open source' %} 20 | 21 | .. toctree:: 22 | :hidden: 23 | 24 | License 25 | {%- endif %} 26 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: shell 2 | 3 | ============ 4 | Installation 5 | ============ 6 | 7 | 8 | Stable release 9 | -------------- 10 | 11 | To install {{ cookiecutter.project_name }}, run this command in your terminal: 12 | 13 | .. code-block:: console 14 | 15 | $ pip install {{ cookiecutter.project_name }} 16 | 17 | This is the preferred method to install {{ cookiecutter.project_name }}, as it will always install the most recent stable release. 18 | 19 | If you don't have `pip`_ installed, this `Python installation guide`_ can guide 20 | you through the process. 21 | 22 | .. _pip: https://pip.pypa.io 23 | .. _Python installation guide: http://docs.python-guide.org/en/latest/starting/installation/ 24 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx==7.4.7 2 | recommonmark==0.7.1 3 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/docs/usage.rst: -------------------------------------------------------------------------------- 1 | ===== 2 | Usage 3 | ===== 4 | 5 | To use {{ cookiecutter.project_name }} in a project:: 6 | 7 | import {{ cookiecutter.project_slug }} 8 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/noxfile.py: -------------------------------------------------------------------------------- 1 | """Nox sessions.""" 2 | 3 | import platform 4 | 5 | import nox 6 | from nox_poetry import Session, session 7 | 8 | nox.options.sessions = ["tests", "mypy"] 9 | python_versions = ["3.9", "3.10", "3.11", "3.12"] 10 | 11 | 12 | @session(python=python_versions) 13 | def tests(session: Session) -> None: 14 | """Run the test suite.""" 15 | session.install(".") 16 | session.install("invoke", "pytest", "xdoctest", "coverage[toml]", "pytest-cov") 17 | try: 18 | session.run( 19 | "inv", 20 | "tests", 21 | env={ 22 | "COVERAGE_FILE": f".coverage.{platform.system()}.{platform.python_version()}", 23 | }, 24 | ) 25 | finally: 26 | if session.interactive: 27 | session.notify("coverage") 28 | 29 | 30 | @session(python=python_versions) 31 | def coverage(session: Session) -> None: 32 | """Produce the coverage report.""" 33 | args = session.posargs if session.posargs and len(session._runner.manifest) == 1 else [] 34 | session.install("invoke", "coverage[toml]") 35 | session.run("inv", "coverage", *args) 36 | 37 | 38 | @session(python=python_versions) 39 | def mypy(session: Session) -> None: 40 | """Type-check using mypy.""" 41 | session.install(".") 42 | session.install("invoke", "mypy") 43 | session.run("inv", "mypy") 44 | 45 | 46 | @session(python="3.12") 47 | def security(session: Session) -> None: 48 | """Scan dependencies for insecure packages.""" 49 | session.install("invoke", "safety") 50 | session.run("inv", "security") 51 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/pyproject.toml: -------------------------------------------------------------------------------- 1 | {%- set license_classifiers = { 2 | 'MIT': 'License :: OSI Approved :: MIT License', 3 | 'BSD': 'License :: OSI Approved :: BSD License', 4 | 'ISC': 'License :: OSI Approved :: ISC License (ISCL)', 5 | 'Apache Software License 2.0': 'License :: OSI Approved :: Apache Software License', 6 | 'GNU General Public License v3': 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)' 7 | } -%} 8 | [tool.poetry] 9 | name = "{{ cookiecutter.project_name }}" 10 | version = "{{ cookiecutter.version }}" 11 | description = "{{ cookiecutter.project_short_description }}" 12 | authors = ["{{ cookiecutter.full_name }} <{{ cookiecutter.email }}>"] 13 | {% if cookiecutter.open_source_license == "Not open source" -%} 14 | license = "{{ cookiecutter.open_source_license }}" 15 | {% endif -%} 16 | readme = "README.md" 17 | homepage = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}" 18 | repository = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}" 19 | documentation = "https://{{ cookiecutter.project_name }}.readthedocs.io" 20 | keywords = ["{{ cookiecutter.project_name }}"] 21 | classifiers=[ 22 | "Development Status :: 2 - Pre-Alpha", 23 | "Intended Audience :: Developers", 24 | {%- if cookiecutter.open_source_license in license_classifiers %} 25 | "{{ license_classifiers[cookiecutter.open_source_license] }}", 26 | {%- endif %} 27 | "Natural Language :: English", 28 | "Programming Language :: Python :: 3", 29 | "Programming Language :: Python :: 3.9", 30 | "Programming Language :: Python :: 3.10", 31 | "Programming Language :: Python :: 3.11", 32 | "Programming Language :: Python :: 3.12", 33 | ] 34 | {%- if cookiecutter.project_name.lower().replace('-', '_') != cookiecutter.project_slug %} 35 | packages = [ 36 | { include = "{{ cookiecutter.project_slug }}", from = "src" }, 37 | ] 38 | {%- endif %} 39 | 40 | [tool.poetry.urls] 41 | "Bug Tracker" = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}/issues" 42 | 43 | {%- if cookiecutter.command_line_interface|lower == 'typer' %} 44 | 45 | [tool.poetry.scripts] 46 | {{ cookiecutter.project_name }} = '{{ cookiecutter.project_slug }}.cli:app' 47 | {%- endif %} 48 | 49 | [tool.poetry.dependencies] 50 | python = "<3.13,>=3.9" 51 | {%- if cookiecutter.command_line_interface|lower == 'typer' %} 52 | typer = {extras = ["all"], version = "^0.13.0"} 53 | {%- endif %} 54 | 55 | [tool.poetry.group.dev.dependencies] 56 | pre-commit = "^4.0.1" 57 | invoke = "^2.2.0" 58 | bump2version = "^1.0.1" 59 | watchdog = {version = "^6.0.0", extras = ["watchmedo"]} 60 | 61 | [tool.poetry.group.test.dependencies] 62 | pytest = "^8.3.3" 63 | xdoctest = "^1.2.0" 64 | coverage = {version = "^7.6.7", extras = ["toml"]} 65 | pytest-cov = "^6.0.0" 66 | 67 | [tool.poetry.group.linters.dependencies] 68 | isort = "^5.13.2" 69 | black = "^24.10.0" 70 | ruff = "^0.7.4" 71 | 72 | [tool.poetry.group.security.dependencies] 73 | safety = "^3.2.11" 74 | 75 | [tool.poetry.group.typing.dependencies] 76 | mypy = "^1.13.0" 77 | 78 | [tool.poetry.group.docs.dependencies] 79 | sphinx = "^7.4.7" 80 | recommonmark = "^0.7.1" 81 | 82 | [tool.coverage.paths] 83 | source = ["src", "*/site-packages"] 84 | 85 | [tool.coverage.run] 86 | branch = true 87 | source = ["{{ cookiecutter.project_slug }}"] 88 | 89 | [tool.coverage.report] 90 | fail_under = 100 91 | exclude_lines = [ 92 | "pragma: no cover", 93 | "def __repr__", 94 | "if self.debug", 95 | "if settings.DEBUG:", 96 | "raise AssertionError", 97 | "raise NotImplementedError", 98 | "if 0:", 99 | "if __name__ == __main__:" 100 | ] 101 | show_missing = true 102 | 103 | [tool.coverage.html] 104 | directory = "htmlcov" 105 | 106 | [tool.ruff] 107 | target-version = "py39" 108 | output-format = "full" 109 | line-length = 99 110 | fix = true 111 | extend-exclude = [ 112 | "docs/*", 113 | ] 114 | 115 | [tool.ruff.lint] 116 | select = [ 117 | "E", "F", "W", # flake8 118 | "C", # mccabe 119 | "I", # isort 120 | "N", # pep8-naming 121 | "D", # flake8-docstrings 122 | "ANN", # flake8-annotations 123 | "S", # flake8-bandit 124 | "BLE", # flake8-blind-except 125 | "B", # flake8-bugbear 126 | "A", # flake8-builtins 127 | "G", # flake8-logging-format 128 | "ERA", # eradicate 129 | "ISC", # flake8-implicit-str-concat 130 | "RUF", # Ruff-specific rules 131 | ] 132 | ignore = ["ANN101"] 133 | unfixable = [ 134 | "ERA", # Don't remove commented-out code 135 | ] 136 | 137 | [tool.ruff.lint.per-file-ignores] 138 | "tests/*" = ["S101"] 139 | 140 | [tool.ruff.lint.mccabe] 141 | max-complexity = 10 142 | 143 | [tool.ruff.lint.isort] 144 | known-first-party = ["{{ cookiecutter.project_slug }}"] 145 | 146 | [tool.ruff.lint.pydocstyle] 147 | convention = "google" 148 | 149 | [tool.isort] 150 | multi_line_output = 3 151 | include_trailing_comma = true 152 | force_grid_wrap = 0 153 | use_parentheses = true 154 | line_length = 99 155 | known_third_party = ["invoke", "nox", "nox_poetry"] 156 | 157 | [tool.black] 158 | line-length = 99 159 | target-version = ["py39"] 160 | 161 | [tool.mypy] 162 | warn_return_any = true 163 | warn_unused_configs = true 164 | 165 | [[tool.mypy.overrides]] 166 | module = ["pytest.*", "invoke.*", "nox.*", "nox_poetry.*"] 167 | allow_redefinition = false 168 | check_untyped_defs = true 169 | ignore_errors = false 170 | ignore_missing_imports = true 171 | implicit_reexport = true 172 | local_partial_types = true 173 | strict_optional = true 174 | strict_equality = true 175 | no_implicit_optional = true 176 | warn_unused_ignores = true 177 | warn_unreachable = true 178 | warn_no_return = true 179 | 180 | [build-system] 181 | requires = ["poetry>=0.12"] 182 | build-backend = "poetry.masonry.api" 183 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/__init__.py: -------------------------------------------------------------------------------- 1 | """Top-level package for {{ cookiecutter.project_name }}.""" 2 | 3 | __author__ = """{{ cookiecutter.full_name }}""" 4 | __email__ = "{{ cookiecutter.email }}" 5 | __version__ = "{{ cookiecutter.version }}" 6 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/cli.py: -------------------------------------------------------------------------------- 1 | """Console script for {{ cookiecutter.project_name }}.""" 2 | 3 | from typing import Annotated, Optional 4 | 5 | import typer 6 | 7 | from {{ cookiecutter.project_slug }} import __version__ 8 | 9 | app = typer.Typer() 10 | 11 | 12 | def version_callback(value: bool) -> None: 13 | """Callback function for the --version option. 14 | 15 | Parameters: 16 | - value: The value provided for the --version option. 17 | 18 | Raises: 19 | - typer.Exit: Raises an Exit exception if the --version option is provided, 20 | printing the Awesome CLI version and exiting the program. 21 | """ 22 | if value: 23 | typer.echo(f"{{ cookiecutter.project_name }}, version {__version__}") 24 | raise typer.Exit() 25 | 26 | 27 | @app.command() 28 | def main( 29 | version: Annotated[ 30 | Optional[bool], typer.Option("--version", callback=version_callback, is_eager=True) 31 | ] = None, 32 | ) -> None: 33 | """Console script for {{ cookiecutter.project_name }}.""" 34 | typer.echo("Replace this message by putting your code into {{ cookiecutter.project_slug }}.cli.main") 35 | typer.echo("See typer documentation at https://typer.tiangolo.com/") 36 | 37 | 38 | if __name__ == "__main__": 39 | app() # pragma: no cover 40 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedejaure/cookiecutter-modern-pypackage/d4e1d7bb0c5374beaf38314f7471f44ae81bf48d/{{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/py.typed -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/{{cookiecutter.project_slug}}.py: -------------------------------------------------------------------------------- 1 | """Main module.""" 2 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/tasks.py: -------------------------------------------------------------------------------- 1 | """Tasks for maintaining the project. 2 | 3 | Execute 'invoke --list' for guidance on using Invoke 4 | """ 5 | 6 | import platform 7 | import webbrowser 8 | from pathlib import Path 9 | from typing import Optional 10 | 11 | from invoke import call, task 12 | from invoke.context import Context 13 | from invoke.runners import Result 14 | 15 | ROOT_DIR = Path(__file__).parent 16 | DOCS_DIR = ROOT_DIR.joinpath("docs") 17 | DOCS_BUILD_DIR = DOCS_DIR.joinpath("_build") 18 | DOCS_INDEX = DOCS_BUILD_DIR.joinpath("index.html") 19 | COVERAGE_FILE = ROOT_DIR.joinpath(".coverage") 20 | COVERAGE_DIR = ROOT_DIR.joinpath("htmlcov") 21 | COVERAGE_REPORT = COVERAGE_DIR.joinpath("index.html") 22 | SOURCE_DIR = ROOT_DIR.joinpath("src/{{ cookiecutter.project_slug }}") 23 | TEST_DIR = ROOT_DIR.joinpath("tests") 24 | PYTHON_TARGETS = [ 25 | SOURCE_DIR, 26 | TEST_DIR, 27 | DOCS_DIR.joinpath("conf.py"), 28 | ROOT_DIR.joinpath("noxfile.py"), 29 | Path(__file__), 30 | ] 31 | PYTHON_TARGETS_STR = " ".join([str(p) for p in PYTHON_TARGETS]) 32 | 33 | 34 | def _run(c: Context, command: str) -> Optional[Result]: 35 | return c.run(command, pty=platform.system() != "Windows") 36 | 37 | 38 | @task() 39 | def clean_build(c: Context) -> None: 40 | """Clean up files from package building.""" 41 | _run(c, "rm -fr build/") 42 | _run(c, "rm -fr dist/") 43 | _run(c, "rm -fr .eggs/") 44 | _run(c, "find . -name '*.egg-info' -exec rm -fr {} +") 45 | _run(c, "find . -name '*.egg' -exec rm -f {} +") 46 | 47 | 48 | @task() 49 | def clean_python(c: Context) -> None: 50 | """Clean up python file artifacts.""" 51 | _run(c, "find . -name '*.pyc' -exec rm -f {} +") 52 | _run(c, "find . -name '*.pyo' -exec rm -f {} +") 53 | _run(c, "find . -name '*~' -exec rm -f {} +") 54 | _run(c, "find . -name '__pycache__' -exec rm -fr {} +") 55 | 56 | 57 | @task() 58 | def clean_tests(c: Context) -> None: 59 | """Clean up files from testing.""" 60 | _run(c, f"rm -f {COVERAGE_FILE}") 61 | _run(c, f"rm -fr {COVERAGE_DIR}") 62 | _run(c, "rm -fr .pytest_cache") 63 | 64 | 65 | @task() 66 | def clean_docs(c: Context) -> None: 67 | """Clean up files from documentation builds.""" 68 | _run(c, f"rm -fr {DOCS_BUILD_DIR}") 69 | 70 | 71 | @task(pre=[clean_build, clean_python, clean_tests, clean_docs]) 72 | def clean(c: Context) -> None: 73 | """Run all clean sub-tasks.""" 74 | 75 | 76 | @task() 77 | def install_hooks(c: Context) -> None: 78 | """Install pre-commit hooks.""" 79 | _run(c, "poetry run pre-commit install") 80 | 81 | 82 | @task() 83 | def hooks(c: Context) -> None: 84 | """Run pre-commit hooks.""" 85 | _run(c, "poetry run pre-commit run --all-files") 86 | 87 | 88 | @task(name="format", help={"check": "Checks if source is formatted without applying changes"}) 89 | def format_(c: Context, check: bool = False) -> None: 90 | """Format code.""" 91 | isort_options = ["--check-only", "--diff"] if check else [] 92 | _run(c, f"poetry run isort {' '.join(isort_options)} {PYTHON_TARGETS_STR}") 93 | black_options = ["--diff", "--check"] if check else ["--quiet"] 94 | _run(c, f"poetry run black {' '.join(black_options)} {PYTHON_TARGETS_STR}") 95 | 96 | 97 | @task() 98 | def ruff(c: Context) -> None: 99 | """Run ruff.""" 100 | _run(c, f"poetry run ruff check {PYTHON_TARGETS_STR}") 101 | 102 | 103 | @task() 104 | def security(c: Context) -> None: 105 | """Run security related checks.""" 106 | _run( 107 | c, 108 | "poetry export --with dev --format=requirements.txt --without-hashes | " 109 | "poetry run safety check --stdin --full-report", 110 | ) 111 | 112 | 113 | @task(pre=[ruff, security, call(format_, check=True)]) 114 | def lint(c: Context) -> None: 115 | """Run all linting.""" 116 | 117 | 118 | @task() 119 | def mypy(c: Context) -> None: 120 | """Run mypy.""" 121 | _run(c, f"poetry run mypy {PYTHON_TARGETS_STR}") 122 | 123 | 124 | @task() 125 | def tests(c: Context) -> None: 126 | """Run tests.""" 127 | pytest_options = ["--xdoctest", "--cov", "--cov-report=", "--cov-fail-under=0"] 128 | _run(c, f"poetry run pytest {' '.join(pytest_options)} {TEST_DIR} {SOURCE_DIR}") 129 | 130 | 131 | @task( 132 | help={ 133 | "fmt": "Build a local report: report, html, json, annotate, html, xml.", 134 | "open_browser": "Open the coverage report in the web browser (requires --fmt html)", 135 | } 136 | ) 137 | def coverage(c: Context, fmt: str = "report", open_browser: bool = False) -> None: 138 | """Create coverage report.""" 139 | if any(Path().glob(".coverage.*")): 140 | _run(c, "poetry run coverage combine") 141 | _run(c, f"poetry run coverage {fmt} -i") 142 | if fmt == "html" and open_browser: 143 | webbrowser.open(COVERAGE_REPORT.as_uri()) 144 | 145 | 146 | @task( 147 | help={ 148 | "serve": "Build the docs watching for changes", 149 | "open_browser": "Open the docs in the web browser", 150 | } 151 | ) 152 | def docs(c: Context, serve: bool = False, open_browser: bool = False) -> None: 153 | """Build documentation.""" 154 | _run(c, f"sphinx-apidoc -o {DOCS_DIR} {SOURCE_DIR}") 155 | build_docs = f"sphinx-build -b html {DOCS_DIR} {DOCS_BUILD_DIR}" 156 | _run(c, build_docs) 157 | if open_browser: 158 | webbrowser.open(DOCS_INDEX.absolute().as_uri()) 159 | if serve: 160 | _run(c, f"poetry run watchmedo shell-command -p '*.rst;*.md' -c '{build_docs}' -R -D .") 161 | 162 | 163 | @task( 164 | help={ 165 | "part": "Part of the version to be bumped.", 166 | "dry_run": "Don't write any files, just pretend. (default: False)", 167 | } 168 | ) 169 | def version(c: Context, part: str, dry_run: bool = False) -> None: 170 | """Bump version.""" 171 | bump_options = ["--dry-run"] if dry_run else [] 172 | _run(c, f"poetry run bump2version {' '.join(bump_options)} {part}") 173 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Unit test package for {{ cookiecutter.project_slug }}.""" 2 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/tests/test_cli.py: -------------------------------------------------------------------------------- 1 | """Tests for `{{ cookiecutter.project_slug }}`.cli module.""" 2 | 3 | from typing import List 4 | 5 | import pytest 6 | from typer.testing import CliRunner 7 | 8 | import {{ cookiecutter.project_slug }} 9 | from {{ cookiecutter.project_slug }} import cli 10 | 11 | runner = CliRunner() 12 | 13 | 14 | @pytest.mark.parametrize( 15 | "options,expected", 16 | [ 17 | ([], "{{ cookiecutter.project_slug }}.cli.main"), 18 | (["--help"], "Usage: "), 19 | ( 20 | ["--version"], 21 | f"{{ cookiecutter.project_name }}, version { {{ cookiecutter.project_slug }}.__version__ }\n", 22 | ), 23 | ], 24 | ) 25 | def test_command_line_interface(options: List[str], expected: str) -> None: 26 | """Test the CLI.""" 27 | result = runner.invoke(cli.app, options) 28 | assert result.exit_code == 0 29 | assert expected in result.stdout 30 | -------------------------------------------------------------------------------- /{{cookiecutter.project_name}}/tests/test_{{cookiecutter.project_slug}}.py: -------------------------------------------------------------------------------- 1 | """Tests for `{{ cookiecutter.project_slug }}` module.""" 2 | 3 | from typing import Generator 4 | 5 | import pytest 6 | 7 | import {{ cookiecutter.project_slug }} 8 | 9 | 10 | @pytest.fixture 11 | def version() -> Generator[str, None, None]: 12 | """Sample pytest fixture.""" 13 | yield {{ cookiecutter.project_slug }}.__version__ 14 | 15 | 16 | def test_version(version: str) -> None: 17 | """Sample pytest test function with the pytest fixture as an argument.""" 18 | assert version == "{{ cookiecutter.version }}" 19 | --------------------------------------------------------------------------------