├── test
├── __init__.py
└── unit
│ ├── __init__.py
│ ├── conftest.py
│ └── test_my_module.py
├── project_name
├── py.typed
├── _version.py
├── __init__.py
└── my_module.py
├── .flake8
├── CHANGELOG.md
├── .coveragerc
├── .gitignore
├── .github
└── workflows
│ ├── release.yml
│ └── build.yml
├── LICENSE
├── README_project.md
├── pyproject.toml
├── justfile
└── README.md
/test/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/project_name/py.typed:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/unit/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/project_name/_version.py:
--------------------------------------------------------------------------------
1 | __version__ = '0.1.0'
2 |
--------------------------------------------------------------------------------
/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | max-line-length = 120
3 | extend-ignore = E203
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | ## v0.1.0 (2024-10-10)
4 |
5 | - Details go here
6 |
--------------------------------------------------------------------------------
/test/unit/conftest.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 |
4 | @pytest.fixture
5 | def mock_function():
6 | pass
7 |
--------------------------------------------------------------------------------
/test/unit/test_my_module.py:
--------------------------------------------------------------------------------
1 | import project_name
2 |
3 |
4 | def test_main():
5 | project_name.main()
6 |
--------------------------------------------------------------------------------
/project_name/__init__.py:
--------------------------------------------------------------------------------
1 | from project_name.my_module import main
2 |
3 |
4 | __all__ = [
5 | 'main',
6 | ]
7 |
--------------------------------------------------------------------------------
/.coveragerc:
--------------------------------------------------------------------------------
1 | [report]
2 | exclude_lines =
3 | if __name__ == '__main__':
4 | [run]
5 | omit = project_name/_version.py
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | .coverage
3 | .DS_Store
4 | .env
5 | *.egg-info
6 | *.lcov
7 | build
8 | dist
9 | htmlcov
10 | venv
11 |
--------------------------------------------------------------------------------
/project_name/my_module.py:
--------------------------------------------------------------------------------
1 | def main():
2 | """The main entrypoint for this script used in the setup.py file."""
3 | pass
4 |
5 |
6 | if __name__ == '__main__':
7 | main()
8 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | # name: release
2 |
3 | # on:
4 | # release:
5 | # types: [published]
6 | # workflow_dispatch: ~
7 |
8 | # jobs:
9 | # release:
10 | # runs-on: ubuntu-latest
11 | # steps:
12 | # - uses: actions/checkout@v5
13 | # - uses: extractions/setup-just@v3
14 | # - uses: actions/setup-python@v6
15 | # with:
16 | # python-version: '3.14'
17 | # - name: Build package
18 | # run: just install build
19 | # - name: Publish to PyPI
20 | # uses: pypa/gh-action-pypi-publish@release/v1
21 | # with:
22 | # password: ${{ secrets.PYPI_API_TOKEN }}
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Justin Hammond
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README_project.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # PROJECT_NAME_URL
4 |
5 | A one-liner description of your project goes here.
6 |
7 | [](https://github.com/USERNAME/PROJECT_NAME_URL/actions)
8 | [](https://app.codecov.io/github/USERNAME/PROJECT_NAME_URL)
9 | [](https://pypi.org/project/PROJECT_NAME_URL)
10 | [](LICENSE)
11 |
12 |

13 |
14 |
15 |
16 | A longer paragraph description of your project goes here.
17 |
18 | ## Install
19 |
20 | ```bash
21 | # Install tool
22 | pip3 install project_name
23 |
24 | # Install locally
25 | just install
26 | ```
27 |
28 | ## Usage
29 |
30 | Usage instructions go here.
31 |
32 | ```bash
33 | venv/bin/python my_script.py
34 | ```
35 |
36 | ## Development
37 |
38 | ```bash
39 | # Get a comprehensive list of development tools
40 | just --list
41 | ```
42 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools>=61", "wheel"]
3 | build-backend = "setuptools.build_meta"
4 |
5 | [project]
6 | name = "PROJECT_NAME_URL"
7 | description = "Your project description here"
8 | dynamic = ["version"]
9 | readme = "README.md"
10 | requires-python = ">=3.10,<4"
11 | license = { text = "MIT" }
12 | authors = [{ name = "USERNAME" }]
13 | urls = { Homepage = "http://github.com/USERNAME/PROJECT_NAME_URL" }
14 | scripts = { PROJECT_NAME_URL = "project_name.my_module:main" }
15 | dependencies = [
16 | # Add your list of production dependencies here, eg:
17 | # "requests == 2.*",
18 | ]
19 | optional-dependencies = { dev = [
20 | "bandit == 1.9.*",
21 | "black == 25.*",
22 | "build == 1.3.*",
23 | "flake8 == 7.*",
24 | "isort == 7.*",
25 | "mypy == 1.18.*",
26 | "pytest == 9.*",
27 | "pytest-cov == 7.*",
28 | ] }
29 |
30 | [tool.setuptools.dynamic]
31 | version = { attr = "project_name._version.__version__" }
32 |
33 | [tool.setuptools.packages.find]
34 | exclude = ["examples", "test"]
35 |
36 | [tool.setuptools.package-data]
37 | project_name = ["py.typed"]
38 |
39 | [tool.black]
40 | line-length = 120
41 |
42 | [tool.isort]
43 | profile = "black"
44 | line_length = 120
45 | indent = 4
46 | force_grid_wrap = 2
47 | multi_line_output = 3
48 | sections = "FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER"
49 | lines_after_imports = 2
50 | include_trailing_comma = true
51 | use_parentheses = true
52 |
53 | [tool.mypy]
54 | disable_error_code = "import-untyped"
55 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | # name: build
2 |
3 | # on:
4 | # push:
5 | # paths:
6 | # - '.github/workflows/build.yml'
7 | # - '**/*.py'
8 | # branches:
9 | # - '**'
10 | # tags:
11 | # - '!**'
12 | # pull_request:
13 | # paths:
14 | # - '.github/workflows/build.yml'
15 | # - '**/*.py'
16 | # workflow_dispatch: ~
17 |
18 | # jobs:
19 | # lint:
20 | # runs-on: ubuntu-latest
21 | # steps:
22 | # - uses: actions/checkout@v5
23 | # - uses: extractions/setup-just@v3
24 | # - uses: actions/setup-python@v6
25 | # with:
26 | # python-version: '3.14'
27 | # - run: just install lint
28 | # test:
29 | # runs-on: ubuntu-latest
30 | # strategy:
31 | # matrix:
32 | # pythonversion: ['3.10', '3.11', '3.12', '3.13', '3.14']
33 | # steps:
34 | # - uses: actions/checkout@v5
35 | # - uses: extractions/setup-just@v3
36 | # - uses: actions/setup-python@v6
37 | # with:
38 | # python-version: ${{ matrix.pythonversion }}
39 | # - run: just install coverage
40 | # coverage:
41 | # if: github.ref == 'refs/heads/main'
42 | # runs-on: ubuntu-latest
43 | # steps:
44 | # - uses: actions/checkout@v5
45 | # - uses: extractions/setup-just@v3
46 | # - uses: actions/setup-python@v6
47 | # with:
48 | # python-version: '3.14'
49 | # - run: just install coverage
50 | # - uses: codecov/codecov-action@v5
51 | # with:
52 | # token: ${{ secrets.CODECOV_TOKEN }}
53 |
--------------------------------------------------------------------------------
/justfile:
--------------------------------------------------------------------------------
1 | PYTHON_BINARY := "python3"
2 | VIRTUAL_ENV := "venv"
3 | VIRTUAL_BIN := VIRTUAL_ENV / "bin"
4 | PROJECT_NAME := "project_name"
5 | TEST_DIR := "test"
6 |
7 | # Scans the project for security vulnerabilities
8 | bandit:
9 | {{VIRTUAL_BIN}}/bandit -r {{PROJECT_NAME}}/
10 |
11 | # Builds the project in preparation for release
12 | build:
13 | {{VIRTUAL_BIN}}/python -m build
14 |
15 | # Runs the Black Python formatter against the project
16 | black:
17 | {{VIRTUAL_BIN}}/black {{PROJECT_NAME}}/ {{TEST_DIR}}/
18 |
19 | # Checks if the project is formatted correctly against the Black rules
20 | black-check:
21 | {{VIRTUAL_BIN}}/black {{PROJECT_NAME}}/ {{TEST_DIR}}/ --check
22 |
23 | # Test the project and generate an HTML coverage report
24 | coverage:
25 | {{VIRTUAL_BIN}}/pytest --cov={{PROJECT_NAME}} --cov-branch --cov-report=html --cov-report=lcov --cov-report=term-missing --cov-fail-under=90
26 |
27 | # Cleans the project
28 | clean:
29 | rm -rf {{VIRTUAL_ENV}} dist *.egg-info .coverage htmlcov .*cache
30 | find . -name '*.pyc' -delete
31 |
32 | # Run flake8 checks against the project
33 | flake8:
34 | {{VIRTUAL_BIN}}/flake8 {{PROJECT_NAME}}/ {{TEST_DIR}}/
35 |
36 | # Lints the project
37 | lint: black-check isort-check flake8 mypy bandit
38 |
39 | # Runs all formatting tools against the project
40 | lint-fix: black isort
41 |
42 | # Install the project locally
43 | install:
44 | {{PYTHON_BINARY}} -m venv {{VIRTUAL_ENV}}
45 | {{VIRTUAL_BIN}}/pip install -e ."[dev]"
46 |
47 | # Sorts imports throughout the project
48 | isort:
49 | {{VIRTUAL_BIN}}/isort {{PROJECT_NAME}}/ {{TEST_DIR}}/
50 |
51 | # Checks that imports throughout the project are sorted correctly
52 | isort-check:
53 | {{VIRTUAL_BIN}}/isort {{PROJECT_NAME}}/ {{TEST_DIR}}/ --check-only
54 |
55 | # Run mypy type checking on the project
56 | mypy:
57 | {{VIRTUAL_BIN}}/mypy --install-types --non-interactive {{PROJECT_NAME}}/ {{TEST_DIR}}/
58 |
59 | # Test the project
60 | test:
61 | {{VIRTUAL_BIN}}/pytest
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Python Template
2 |
3 | A Python project template to save you time and energy.
4 |
5 | [](LICENSE)
6 |
7 | Python projects take a long time to setup with all the various files, the virtual environment, and keeping things uniform across projects. With this Python template, you can quickly setup boilerplate code and miscellaneous items for your Python project saving you time and energy so you can get back to coding.
8 |
9 | ## Install
10 |
11 | Click the [Use this template](https://github.com/Justintime50/python-template/generate) button at the top of this project's GitHub page to get started.
12 |
13 | ## Usage
14 |
15 | ### Easy text replacements
16 |
17 | 1. Replace all instances of `project_name` with the name of your project
18 | * These are the Python snake_case references (eg: `project_name`)
19 | 1. Replace all instances of `PROJECT_NAME_URL` with the name of your project
20 | * These are the references to your project that will appear in URLs and are typically hyphenated (eg: `project-name`)
21 | 1. Replace all instances of `USERNAME` with the name of the author or owner of the project
22 | * These are references typically found in the URL of your project as it appears on GitHub
23 |
24 | ### File configuration
25 |
26 | 1. Configure the `setup.py` file
27 | 1. Configure the `justfile` targets
28 | 1. Update the name in the `LICENSE` or swap it out entirely
29 | 1. Configure the `.github/workflows/build.yml` file
30 | 1. Update the `CHANGELOG.md` with your own info
31 | 1. Rename other files/folders as needed and configure their content
32 | 1. Delete this `README` and rename `README_project.md` to `README.md`
33 |
34 | ### GitHub configuration
35 |
36 | 1. Add a `PYPI_API_TOKEN` GitHub secret to your project so that automated releasing can occur from GitHub Actions to PyPI and uncomment the final step on the `release` job in `.github/workflows/release.yml`
37 |
38 | ## Attribution
39 |
40 | * Watch [the video](https://youtu.be/ZMfcl3CnRhA) where I built this template.
41 |
--------------------------------------------------------------------------------