├── .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 |
8 |
9 | [](https://github.com/fedejaure/cookiecutter-modern-pypackage/releases)
10 | [](https://www.python.org/)
11 | [](https://github.com/fedejaure/cookiecutter-modern-pypackage/actions?workflow=tests)
12 | [](https://cookiecutter-modern-pypackage.readthedocs.io/)
13 | [](https://opensource.org/licenses/MIT)
14 |
15 | [](https://github.com/psf/black)
16 | [](https://github.com/pre-commit/pre-commit)
17 | [](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 | [](https://pypi.python.org/pypi/{{ cookiecutter.project_name }})
8 | [](https://pypi.python.org/pypi/{{ cookiecutter.project_name }})
9 | [](https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }}/actions?workflow=tests)
10 | [](https://codecov.io/gh/{{ cookiecutter.github_username }}/{{ cookiecutter.project_name }})
11 | [](https://{{ cookiecutter.project_name }}.readthedocs.io/)
12 | [](https://pypi.python.org/pypi/{{ cookiecutter.project_name }})
13 |
14 | [](https://github.com/psf/black)
15 | [](https://github.com/pre-commit/pre-commit)
16 | {% if cookiecutter.add_code_of_conduct == 'y' %}[](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 |
--------------------------------------------------------------------------------