├── src └── REPLACE_PACKAGE_NAME │ ├── py.typed │ └── __init__.py ├── .github ├── FUNDING.yml ├── pull_request_template.md └── workflows │ └── ci-cd.yaml ├── docs ├── index.md └── license.md ├── .dockerignore ├── tests ├── conftest.py └── test_hello_world.py ├── Dockerfile ├── .pre-commit-config.yaml ├── .devcontainer └── devcontainer.json ├── README.md ├── LICENSE ├── mkdocs.yml ├── .gitignore └── pyproject.toml /src/REPLACE_PACKAGE_NAME/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: REPLACE_GITHUB_USERNAME 2 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # REPLACE_PACKAGE_NAME 2 | 3 | REPLACE_PACKAGE_DESCRIPTION 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .venv 2 | .mypy_cache 3 | .pytest_cache 4 | __pycache__ 5 | .hatch -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.fixture 5 | def hello_world() -> str: 6 | return "hello world!" 7 | -------------------------------------------------------------------------------- /src/REPLACE_PACKAGE_NAME/__init__.py: -------------------------------------------------------------------------------- 1 | """`REPLACE_PACKAGE_NAME `: REPLACE_PACKAGE_DESCRIPTION""" 2 | 3 | __author__ = "REPLACE_FULL_NAME " 4 | __version__ = "0.0.1" 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-slim 2 | 3 | RUN apt-get update \ 4 | && apt-get install build-essential git -y --no-install-recommends 5 | 6 | ENV PYTHONUNBUFFERED=1 7 | 8 | RUN python -m pip install pip --upgrade \ 9 | && python -m pip install hatch 10 | 11 | COPY . . 12 | 13 | # https://github.com/gitpod-io/gitpod/issues/1997 14 | ENV PIP_USER false 15 | 16 | RUN python -m hatch env create 17 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## ✨ Features 2 | 3 | - List 4 | - implemented 5 | - features 6 | - here 7 | 8 | ## 🐛 Bug Fixes 9 | 10 | - Listed 11 | - fixed 12 | - bugs 13 | - here 14 | 15 | ## 🔗 Linked Issue/s 16 | 17 | Add here the reference to the issue/s referenced in this PR 18 | 19 | ## 🧪 Tests 20 | 21 | - [ ] Did you implement unit tests if required? 22 | 23 | If the above checkbox is checked, describe how you unit-tested it. -------------------------------------------------------------------------------- /tests/test_hello_world.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | class TestHelloWorld: 5 | @pytest.fixture(autouse=True) 6 | @pytest.mark.usefixtures("hello_world") 7 | def setup_method(self, hello_world: str) -> None: 8 | self.hello_world = hello_world 9 | 10 | def teardown(self) -> None: 11 | del self.hello_world 12 | 13 | def test_hello_world(self) -> None: 14 | assert isinstance(self.hello_world, str) 15 | assert self.hello_world == "hello world!" 16 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: "v4.3.0" 4 | hooks: 5 | - id: check-added-large-files 6 | - id: check-toml 7 | - id: check-yaml 8 | 9 | - repo: https://github.com/psf/black 10 | rev: 22.10.0 11 | hooks: 12 | - id: black 13 | args: ["--preview"] 14 | language_version: python3 15 | 16 | - repo: https://github.com/charliermarsh/ruff-pre-commit 17 | rev: "v0.0.263" 18 | hooks: 19 | - id: ruff 20 | args: [--fix] 21 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "dockerfile": "../Dockerfile" 4 | }, 5 | "customizations": { 6 | "codespaces": { 7 | "extensions": [ 8 | "ms-python.python", 9 | "ms-python.vscode-pylance", 10 | "eamodio.gitlens", 11 | "bungcip.better-toml", 12 | "ms-azuretools.vscode-docker", 13 | "zhuangtongfa.material-theme", 14 | "redhat.vscode-yaml", 15 | "GitHub.copilot" 16 | ] 17 | }, 18 | "vscode": { 19 | "extensions": [ 20 | "ms-python.python", 21 | "ms-python.vscode-pylance", 22 | "eamodio.gitlens", 23 | "bungcip.better-toml", 24 | "ms-azuretools.vscode-docker", 25 | "zhuangtongfa.material-theme", 26 | "redhat.vscode-yaml", 27 | "GitHub.copilot" 28 | ] 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🎈 Python Package Template 2 | 3 | ## ✨ Features 4 | 5 | * Package configuration with `pyproject.toml` built with `hatch` 6 | * Code formatting and linting with `ruff`, and `black` 7 | * `Dockerfile` with package installation 8 | * `pre-commit` configuration file 9 | * GitHub Codespaces can be created from `.devcontainer` 10 | * CI-CD Pipelines with GitHub Actions 11 | * Basic `pytest` set-up for unit tests 12 | * Auto-generated docs with `mkdocs` and `mkdocs-material` 13 | 14 | ## 🚚 Replacements 15 | 16 | * `REPLACE_PACKAGE_NAME`: name of the package (usually the same name as the repository in which it's hosted). 17 | * `REPLACE_PACKAGE_DESCRIPTION`: description of the package. 18 | * `REPLACE_FULL_NAME`: user's full name. 19 | * `REPLACE_EMAIL`: user's email. 20 | * `REPLACE_GITHUB_USERNAME`: GitHub username of the package owner. 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-present REPLACE_FULL_NAME 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. -------------------------------------------------------------------------------- /docs/license.md: -------------------------------------------------------------------------------- 1 | # 📄 License 2 | 3 | MIT License 4 | 5 | Copyright (c) 2023-present REPLACE_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 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: REPLACE_PACKAGE_NAME 2 | site_url: https://github.com/REPLACE_GITHUB_USERNAME/REPLACE_PACKAGE_NAME 3 | site_author: REPLACE_FULL_NAME 4 | site_description: REPLACE_PACKAGE_DESCRIPTION 5 | 6 | repo_name: REPLACE_GITHUB_USERNAME/REPLACE_PACKAGE_NAME 7 | repo_url: https://github.com/REPLACE_GITHUB_USERNAME/REPLACE_PACKAGE_NAME 8 | 9 | copyright: Copyright (c) 2023-present REPLACE_FULL_NAME 10 | 11 | theme: 12 | name: material 13 | palette: 14 | - scheme: default 15 | toggle: 16 | icon: material/brightness-7 17 | name: Switch to dark mode 18 | - scheme: slate 19 | toggle: 20 | icon: material/brightness-4 21 | name: Switch to light mode 22 | font: 23 | text: Roboto 24 | code: Roboto Mono 25 | 26 | markdown_extensions: 27 | - pymdownx.highlight: 28 | anchor_linenums: true 29 | - pymdownx.superfences 30 | 31 | plugins: 32 | - search: 33 | - git-revision-date-localized: 34 | type: timeago 35 | enable_creation_date: true 36 | - mkdocstrings: 37 | 38 | extra: 39 | social: 40 | - icon: fontawesome/brands/python 41 | link: https://pypi.org/project/REPLACE_PACKAGE_NAME/ 42 | - icon: fontawesome/brands/github 43 | link: https://github.com/REPLACE_GITHUB_USERNAME 44 | # - icon: fontawesome/brands/twitter 45 | # link: https://twitter.com/REPLACE_TWITTER_USERNAME 46 | # - icon: fontawesome/brands/linkedin 47 | # link: https://www.linkedin.com/in/REPLACE_LINKEDIN_USERNAME/ 48 | 49 | nav: 50 | - Home: index.md 51 | - License: license.md 52 | -------------------------------------------------------------------------------- /.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 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # PyCharm default idea folder 107 | .idea/ 108 | 109 | # VSCode Files 110 | .vscode/ 111 | 112 | # Hatch files 113 | .hatch/ 114 | 115 | # Ruff cache 116 | .ruff_cache/ 117 | 118 | # .DS_Store 119 | .DS_Store 120 | -------------------------------------------------------------------------------- /.github/workflows/ci-cd.yaml: -------------------------------------------------------------------------------- 1 | name: ci-cd 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: 7 | - main 8 | push: 9 | branches: 10 | - main 11 | paths: 12 | - .github/workflows/ci-cd.yaml 13 | - src/** 14 | - tests/** 15 | release: 16 | types: 17 | - published 18 | 19 | concurrency: 20 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | check-quality: 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | - name: checkout 29 | uses: actions/checkout@v3 30 | 31 | - name: setup-python 32 | uses: actions/setup-python@v4 33 | with: 34 | python-version: 3.8 35 | 36 | - name: install-dependencies 37 | run: | 38 | python -m pip install --upgrade pip 39 | pip install ".[quality]" 40 | 41 | - name: check-quality 42 | run: | 43 | black --check --diff --preview src tests 44 | ruff src tests 45 | mypy src 46 | 47 | run-tests: 48 | needs: check-quality 49 | 50 | runs-on: ubuntu-latest 51 | 52 | steps: 53 | - name: checkout 54 | uses: actions/checkout@v3 55 | 56 | - name: setup-python 57 | uses: actions/setup-python@v4 58 | with: 59 | python-version: 3.8 60 | 61 | - name: install-dependencies 62 | run: | 63 | python -m pip install --upgrade pip 64 | pip install ".[tests]" 65 | 66 | - name: run-tests 67 | run: pytest --cov=REPLACE_PACKAGE_NAME --cov-report=term-missing tests/ -s --durations 0 68 | 69 | deploy-docs: 70 | needs: run-tests 71 | if: github.event_name == 'release' 72 | 73 | runs-on: ubuntu-latest 74 | 75 | steps: 76 | - name: checkout 77 | uses: actions/checkout@v3 78 | 79 | - name: setup-python 80 | uses: actions/setup-python@v4 81 | with: 82 | python-version: 3.8 83 | 84 | - name: install-dependencies 85 | run: | 86 | python -m pip install --upgrade pip 87 | pip install -e ".[docs]" 88 | 89 | - name: deploy-to-gh-pages 90 | run: mkdocs gh-deploy --force 91 | 92 | publish-package: 93 | needs: deploy-docs 94 | if: github.event_name == 'release' 95 | 96 | runs-on: ubuntu-latest 97 | 98 | steps: 99 | - name: checkout 100 | uses: actions/checkout@v3 101 | 102 | - name: setup-python 103 | uses: actions/setup-python@v4 104 | with: 105 | python-version: 3.8 106 | 107 | - name: install-dependencies 108 | run: | 109 | python -m pip install --upgrade pip 110 | pip install hatch 111 | 112 | - name: build-package 113 | run: hatch build 114 | 115 | - name: publish-package 116 | run: hatch publish --user __token__ --auth $PYPI_TOKEN 117 | env: 118 | PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} 119 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | build-backend = "hatchling.build" 3 | requires = ["hatchling"] 4 | 5 | [project] 6 | authors = [{name = "REPLACE_FULL_NAME"}] 7 | classifiers = [ 8 | "Topic :: Internet", 9 | "Topic :: Software Development :: Libraries :: Application Frameworks", 10 | "Topic :: Software Development :: Libraries :: Python Modules", 11 | "Topic :: Software Development :: Libraries", 12 | "Topic :: Software Development", 13 | "Intended Audience :: Developers", 14 | "License :: OSI Approved :: MIT License", 15 | "Development Status :: 4 - Beta", 16 | "Programming Language :: Python", 17 | "Programming Language :: Python :: 3.8", 18 | "Programming Language :: Python :: 3.9", 19 | "Programming Language :: Python :: 3.10", 20 | "Programming Language :: Python :: 3.11", 21 | "Programming Language :: Python :: Implementation :: CPython", 22 | "Programming Language :: Python :: Implementation :: PyPy", 23 | ] 24 | dependencies = [] 25 | description = "REPLACE_PACKAGE_DESCRIPTION" 26 | dynamic = ["version"] 27 | keywords = [] 28 | license = "MIT" 29 | name = "REPLACE_PACKAGE_NAME" 30 | readme = "README.md" 31 | requires-python = ">=3.8,<3.12" 32 | 33 | [project.urls] 34 | Documentation = "https://REPLACE_GITHUB_USERNAME.github.io/REPLACE_PACKAGE_NAME" 35 | Issues = "https://github.com/REPLACE_GITHUB_USERNAME/REPLACE_PACKAGE_NAME/issues" 36 | Source = "https://github.com/REPLACE_GITHUB_USERNAME/REPLACE_PACKAGE_NAME" 37 | 38 | [tool.hatch.version] 39 | path = "src/REPLACE_PACKAGE_NAME/__init__.py" 40 | 41 | [project.optional-dependencies] 42 | docs = [ 43 | "mkdocs~=1.4.0", 44 | "mkdocs-material~=8.5.4", 45 | "mkdocs-git-revision-date-localized-plugin~=1.1.0", 46 | "mkdocstrings[python]~=0.19.0", 47 | ] 48 | quality = [ 49 | "black~=22.10.0", 50 | "mypy~=1.4.0", 51 | "pre-commit~=2.20.0", 52 | "ruff~=0.0.263", 53 | ] 54 | tests = [ 55 | "pytest~=7.1.2", 56 | "pytest-cov~=4.1", 57 | ] 58 | 59 | [tool.hatch.envs.quality] 60 | features = [ 61 | "quality", 62 | ] 63 | 64 | [tool.hatch.envs.quality.scripts] 65 | check = [ 66 | "black --check --diff --preview src tests", 67 | "ruff src tests", 68 | ] 69 | format = [ 70 | "black --preview src tests", 71 | "ruff --fix src tests", 72 | "check", 73 | ] 74 | typecheck = [ 75 | "mypy src --install-types --non-interactive", 76 | ] 77 | 78 | [tool.mypy] 79 | python_version = "3.8" 80 | check_untyped_defs = true 81 | ignore_missing_imports = true 82 | warn_return_any = true 83 | 84 | [tool.ruff] 85 | ignore = [ 86 | "E501", # line too long, handled by black 87 | "B008", # do not perform function calls in argument defaults 88 | "C901", # too complex 89 | ] 90 | select = [ 91 | "E", # pycodestyle errors 92 | "W", # pycodestyle warnings 93 | "F", # pyflakes 94 | "I", # isort 95 | "C", # flake8-comprehensions 96 | "B", # flake8-bugbear 97 | ] 98 | 99 | [tool.ruff.isort] 100 | known-first-party = ["REPLACE_PACKAGE_NAME"] 101 | 102 | [tool.hatch.envs.tests] 103 | features = [ 104 | "tests", 105 | ] 106 | 107 | [tool.hatch.envs.tests.scripts] 108 | run = "pytest --cov=REPLACE_PACKAGE_NAME --cov-report=term-missing tests/ --durations 0 -s {args:tests}" 109 | 110 | [[tool.hatch.envs.tests.matrix]] 111 | python = ["38", "39", "310", "311"] 112 | 113 | [tool.hatch.envs.docs] 114 | features = [ 115 | "docs", 116 | ] 117 | 118 | [tool.hatch.envs.docs.scripts] 119 | build = [ 120 | "mkdocs build", 121 | ] 122 | serve = [ 123 | "mkdocs serve", 124 | ] 125 | 126 | [tool.hatch.build.targets.sdist] 127 | exclude = [ 128 | "/.github", 129 | "/.vscode", 130 | "/docs", 131 | "/.devcontainer", 132 | "/.pre-commit-config.yaml", 133 | "/.gitignore", 134 | "/tests", 135 | "/Dockerfile", 136 | "/.dockerignore", 137 | ] 138 | --------------------------------------------------------------------------------