├── .editorconfig ├── .gitignore ├── .zed └── settings.json ├── LICENSE ├── README.md ├── cookiecutter.json ├── hooks └── post_gen_project.sh └── {{cookiecutter.dist_name}} ├── .editorconfig ├── .github └── workflows │ ├── build.yml │ └── documentation.yml ├── .gitignore ├── .vscode └── settings.json ├── .zed └── settings.json ├── LICENSE ├── README.md ├── docs ├── index.md └── reference │ └── {{cookiecutter.package_name}}.md ├── mkdocs.yml ├── pyproject.toml ├── tests ├── __init__.py ├── conftest.py └── test_add.py └── {{cookiecutter.package_name}} ├── __init__.py └── py.typed /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | 13 | [*.yml] 14 | indent_size = 2 15 | 16 | [Makefile] 17 | indent_style = tab 18 | -------------------------------------------------------------------------------- /.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 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 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 | junit/ 50 | junit.xml 51 | test.db 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # SageMath parsed files 84 | *.sage.py 85 | 86 | # dotenv 87 | .env 88 | 89 | # virtualenv 90 | .venv 91 | venv/ 92 | ENV/ 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | # mypy 105 | .mypy_cache/ 106 | 107 | # .vscode 108 | .vscode/ 109 | 110 | # OS files 111 | .DS_Store 112 | 113 | # Test project 114 | holy-grail/ 115 | -------------------------------------------------------------------------------- /.zed/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_on_save": "off" 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 François Voron 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cookiecutter Hipster PyPackage 2 | 3 | Cookiecutter template for a cutting-edge Python package: Hatch, ruff, mypy, GitHub Actions and more! 4 | 5 | ## Features 6 | 7 | * [X] Lightweight starter 8 | * [X] [Hatch](https://hatch.pypa.io/latest/install/) package management 9 | * [X] Linting and formatting with [`ruff`](https://github.com/charliermarsh/ruff) 10 | * [X] Type checking with [`mypy`](https://github.com/python/mypy) 11 | * [X] Unit tests with [`pytest`](https://github.com/pytest-dev/pytest) with optional asyncio setup. 12 | * [X] Documentation with [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) and docstring reference support with [mkdocstrings](https://mkdocstrings.github.io/). 13 | * [X] Ready-to-use [GitHub Actions](https://help.github.com/en/actions/automating-your-workflow-with-github-actions) pipelines 14 | 15 | ## Quickstart 16 | 17 | Generate the project: 18 | 19 | ```bash 20 | cookiecutter https://github.com/frankie567/cookiecutter-hipster-pypackage 21 | ``` 22 | 23 | The generator will automatically call `hatch env create` at the end. 24 | 25 | Then, for the GitHub Actions pipelines to work correctly, you should: 26 | 27 | * Enable the GitHub repository in Codecov. 28 | * Set `CODECOV_TOKEN` in your GitHub repository secrets. You can find in the Codecov settings of the corresponding project. 29 | * Enable GitHub Pages using the GitHub Actions source. 30 | * Configure the [Trusted Publisher method on PyPI](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/): it's a modern and secure method to push your package to PyPI. 31 | 32 | ### With cruft 33 | 34 | [cruft](https://github.com/cruft/cruft) is a layer above Cookiecutter allowing you to update your project from the template after it has been generated. 35 | 36 | ```bash 37 | cruft create https://github.com/frankie567/cookiecutter-hipster-pypackage 38 | ``` 39 | 40 | ## License 41 | 42 | This project is licensed under the terms of the MIT license. 43 | -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "full_name": "King Arthur", 3 | "email": "king.arthur@camelot.bt", 4 | "github_username": "king-arthur", 5 | "project_name": "Holy Grail", 6 | "dist_name": "{{cookiecutter.project_name.lower().replace(' ', '-')}}", 7 | "package_name": "{{cookiecutter.dist_name.replace('-', '_')}}", 8 | "project_short_description": "", 9 | "repository_name": "{{cookiecutter.github_username}}/{{cookiecutter.dist_name}}", 10 | "repository_url": "https://github.com/{{cookiecutter.repository_name}}", 11 | "docs_url": "https://{{cookiecutter.github_username}}.github.io/{{cookiecutter.dist_name}}/", 12 | "open_source_license": [ 13 | "MIT license", 14 | "BSD license", 15 | "ISC license", 16 | "Apache Software License 2.0", 17 | "GNU General Public License v3", 18 | "Not open source" 19 | ], 20 | "python_version": "3.11", 21 | "async": ["asyncio", "anyio", "none"], 22 | "docs_icon": "material/library", 23 | "docs_color_primary": [ 24 | "red", 25 | "pink", 26 | "purple", 27 | "deep purple", 28 | "indigo", 29 | "blue", 30 | "light blue", 31 | "cyan", 32 | "teal", 33 | "green", 34 | "light green", 35 | "lime", 36 | "yellow", 37 | "amber", 38 | "orange", 39 | "deep orange", 40 | "brown", 41 | "grey", 42 | "blue grey", 43 | "black", 44 | "white" 45 | ], 46 | "docs_color_accent": [ 47 | "red", 48 | "pink", 49 | "purple", 50 | "deep purple", 51 | "indigo", 52 | "blue", 53 | "light blue", 54 | "cyan", 55 | "teal", 56 | "green", 57 | "light green", 58 | "lime", 59 | "yellow", 60 | "amber", 61 | "orange", 62 | "deep orange" 63 | ] 64 | } 65 | -------------------------------------------------------------------------------- /hooks/post_gen_project.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | pip install hatch 6 | 7 | hatch env create 8 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | python_version: ['{{ cookiecutter.python_version }}'] 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Set up Python 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: {% raw %}${{ matrix.python_version }}{% endraw %} 19 | - name: Install dependencies 20 | run: | 21 | python -m pip install --upgrade pip 22 | pip install hatch 23 | hatch env create 24 | - name: Lint and typecheck 25 | run: | 26 | hatch run lint-check 27 | - name: Test 28 | run: | 29 | hatch run test-cov-xml 30 | - uses: codecov/codecov-action@v5 31 | with: 32 | token: {% raw %}${{ secrets.CODECOV_TOKEN }}{% endraw %} 33 | fail_ci_if_error: true 34 | verbose: true 35 | 36 | release: 37 | runs-on: ubuntu-latest 38 | environment: release 39 | needs: test 40 | if: startsWith(github.ref, 'refs/tags/') 41 | permissions: 42 | contents: write 43 | id-token: write 44 | 45 | steps: 46 | - uses: actions/checkout@v4 47 | - name: Set up Python 48 | uses: actions/setup-python@v5 49 | with: 50 | python-version: '{{ cookiecutter.python_version }}' 51 | - name: Install dependencies 52 | shell: bash 53 | run: | 54 | python -m pip install --upgrade pip 55 | pip install hatch 56 | - name: mint API token 57 | id: mint-token 58 | run: | 59 | # retrieve the ambient OIDC token 60 | resp=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ 61 | "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=pypi") 62 | oidc_token=$(jq -r '.value' <<< "${resp}") 63 | 64 | # exchange the OIDC token for an API token 65 | resp=$(curl -X POST https://pypi.org/_/oidc/mint-token -d "{\"token\": \"${oidc_token}\"}") 66 | api_token=$(jq -r '.token' <<< "${resp}") 67 | 68 | # mask the newly minted API token, so that we don't accidentally leak it 69 | echo "::add-mask::${api_token}" 70 | 71 | # see the next step in the workflow for an example of using this step output 72 | echo "api-token=${api_token}" >> "${GITHUB_OUTPUT}" 73 | - name: Build and publish on PyPI 74 | env: 75 | HATCH_INDEX_USER: __token__ 76 | HATCH_INDEX_AUTH: {% raw %}${{ steps.mint-token.outputs.api-token }}{% endraw %} 77 | run: | 78 | hatch build 79 | hatch publish 80 | - name: Create release 81 | uses: ncipollo/release-action@v1 82 | with: 83 | draft: true 84 | body: {% raw %}${{ github.event.head_commit.message }}{% endraw %} 85 | artifacts: dist/*.whl,dist/*.tar.gz 86 | token: {% raw %}${{ secrets.GITHUB_TOKEN }}{% endraw %} 87 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | name: Build documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | # Allow one concurrent deployment 15 | concurrency: 16 | group: "pages" 17 | cancel-in-progress: true 18 | 19 | # Default to bash 20 | defaults: 21 | run: 22 | shell: bash 23 | 24 | jobs: 25 | build: 26 | 27 | runs-on: ubuntu-latest 28 | 29 | steps: 30 | - uses: actions/checkout@v4 31 | - name: Set up Python 32 | uses: actions/setup-python@v5 33 | with: 34 | python-version: '{{ cookiecutter.python_version }}' 35 | - name: Install dependencies 36 | run: | 37 | python -m pip install --upgrade pip 38 | pip install hatch 39 | hatch env create 40 | - name: Build 41 | run: hatch run docs-build 42 | - name: Upload artifact 43 | uses: actions/upload-pages-artifact@v3 44 | with: 45 | path: ./site 46 | 47 | deploy: 48 | environment: 49 | name: github-pages 50 | url: {% raw %}${{ steps.deployment.outputs.page_url }}{% endraw %} 51 | runs-on: ubuntu-latest 52 | needs: build 53 | steps: 54 | - name: Deploy to GitHub Pages 55 | id: deployment 56 | uses: actions/deploy-pages@v4 57 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_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 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 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 | junit/ 50 | junit.xml 51 | test.db 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # SageMath parsed files 84 | *.sage.py 85 | 86 | # dotenv 87 | .env 88 | 89 | # virtualenv 90 | .venv 91 | venv/ 92 | ENV/ 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | # mypy 105 | .mypy_cache/ 106 | 107 | # OS files 108 | .DS_Store 109 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.analysis.typeCheckingMode": "basic", 3 | "python.analysis.autoImportCompletions": true, 4 | "python.terminal.activateEnvironment": true, 5 | "python.terminal.activateEnvInCurrentTerminal": true, 6 | "python.testing.unittestEnabled": false, 7 | "python.testing.pytestEnabled": true, 8 | "editor.rulers": [88], 9 | "python.defaultInterpreterPath": "${workspaceFolder}/.hatch/{{cookiecutter.dist_name}}/bin/python", 10 | "python.testing.pytestPath": "${workspaceFolder}/.hatch/{{cookiecutter.dist_name}}/bin/pytest", 11 | "python.testing.cwd": "${workspaceFolder}", 12 | "python.testing.pytestArgs": ["--no-cov"], 13 | "[python]": { 14 | "editor.formatOnSave": true, 15 | "editor.codeActionsOnSave": { 16 | "source.fixAll": "explicit", 17 | "source.organizeImports": "explicit" 18 | }, 19 | "editor.defaultFormatter": "charliermarsh.ruff" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/.zed/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "languages": { 3 | "Python": { 4 | "language_servers": ["pyright", "ruff", "!pylsp"], 5 | "format_on_save": "on", 6 | "wrap_guides": [88], 7 | "formatter": [ 8 | { 9 | "code_actions": { 10 | "source.organizeImports.ruff": true, 11 | "source.fixAll.ruff": true 12 | } 13 | }, 14 | { 15 | "language_server": { 16 | "name": "ruff" 17 | } 18 | } 19 | ] 20 | } 21 | }, 22 | "lsp": { 23 | "pyright": { 24 | "settings": { 25 | "python": { 26 | "pythonPath": ".hatch/{{cookiecutter.dist_name}}/bin/python" 27 | } 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/LICENSE: -------------------------------------------------------------------------------- 1 | {% if cookiecutter.open_source_license == 'MIT license' -%} 2 | MIT License 3 | 4 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }} 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 | {% elif cookiecutter.open_source_license == 'BSD license' %} 24 | 25 | BSD License 26 | 27 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }} 28 | All rights reserved. 29 | 30 | Redistribution and use in source and binary forms, with or without modification, 31 | are permitted provided that the following conditions are met: 32 | 33 | * Redistributions of source code must retain the above copyright notice, this 34 | list of conditions and the following disclaimer. 35 | 36 | * Redistributions in binary form must reproduce the above copyright notice, this 37 | list of conditions and the following disclaimer in the documentation and/or 38 | other materials provided with the distribution. 39 | 40 | * Neither the name of the copyright holder nor the names of its 41 | contributors may be used to endorse or promote products derived from this 42 | software without specific prior written permission. 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 45 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 46 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 47 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 48 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 49 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 50 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 51 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 52 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 53 | OF THE POSSIBILITY OF SUCH DAMAGE. 54 | {% elif cookiecutter.open_source_license == 'ISC license' -%} 55 | ISC License 56 | 57 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }} 58 | 59 | 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. 60 | 61 | 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. 62 | {% elif cookiecutter.open_source_license == 'Apache Software License 2.0' -%} 63 | Apache Software License 2.0 64 | 65 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.full_name }} 66 | 67 | Licensed under the Apache License, Version 2.0 (the "License"); 68 | you may not use this file except in compliance with the License. 69 | You may obtain a copy of the License at 70 | 71 | http://www.apache.org/licenses/LICENSE-2.0 72 | 73 | Unless required by applicable law or agreed to in writing, software 74 | distributed under the License is distributed on an "AS IS" BASIS, 75 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 76 | See the License for the specific language governing permissions and 77 | limitations under the License. 78 | {% elif cookiecutter.open_source_license == 'GNU General Public License v3' -%} 79 | GNU GENERAL PUBLIC LICENSE 80 | Version 3, 29 June 2007 81 | 82 | {{ cookiecutter.project_short_description }} 83 | Copyright (C) {% now 'local', '%Y' %} {{ cookiecutter.full_name }} 84 | 85 | This program is free software: you can redistribute it and/or modify 86 | it under the terms of the GNU General Public License as published by 87 | the Free Software Foundation, either version 3 of the License, or 88 | (at your option) any later version. 89 | 90 | This program is distributed in the hope that it will be useful, 91 | but WITHOUT ANY WARRANTY; without even the implied warranty of 92 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 93 | GNU General Public License for more details. 94 | 95 | You should have received a copy of the GNU General Public License 96 | along with this program. If not, see . 97 | 98 | Also add information on how to contact you by electronic and paper mail. 99 | 100 | You should also get your employer (if you work as a programmer) or school, 101 | if any, to sign a "copyright disclaimer" for the program, if necessary. 102 | For more information on this, and how to apply and follow the GNU GPL, see 103 | . 104 | 105 | The GNU General Public License does not permit incorporating your program 106 | into proprietary programs. If your program is a subroutine library, you 107 | may consider it more useful to permit linking proprietary applications with 108 | the library. If this is what you want to do, use the GNU Lesser General 109 | Public License instead of this License. But first, please read 110 | . 111 | {% endif %} 112 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/README.md: -------------------------------------------------------------------------------- 1 | # {{cookiecutter.project_name}} 2 | 3 |

4 | {{cookiecutter.project_short_description}} 5 |

6 | 7 | [![build]({{cookiecutter.repository_url}}/workflows/Build/badge.svg)]({{cookiecutter.repository_url}}/actions) 8 | [![codecov](https://codecov.io/gh/{{cookiecutter.repository_name}}/branch/master/graph/badge.svg)](https://codecov.io/gh/{{cookiecutter.repository_name}}) 9 | [![PyPI version](https://badge.fury.io/py/{{cookiecutter.dist_name}}.svg)](https://badge.fury.io/py/{{cookiecutter.dist_name}}) 10 | 11 | --- 12 | 13 | **Documentation**: {{cookiecutter.docs_url}} 14 | 15 | **Source Code**: {{cookiecutter.repository_url}} 16 | 17 | --- 18 | 19 | ## Development 20 | 21 | ### Setup environment 22 | 23 | We use [Hatch](https://hatch.pypa.io/latest/install/) to manage the development environment and production build. Ensure it's installed on your system. 24 | 25 | ### Run unit tests 26 | 27 | You can run all the tests with: 28 | 29 | ```bash 30 | hatch run test 31 | ``` 32 | 33 | ### Format the code 34 | 35 | Execute the following command to apply linting and check typing: 36 | 37 | ```bash 38 | hatch run lint 39 | ``` 40 | 41 | ### Publish a new version 42 | 43 | You can bump the version, create a commit and associated tag with one command: 44 | 45 | ```bash 46 | hatch version patch 47 | ``` 48 | 49 | ```bash 50 | hatch version minor 51 | ``` 52 | 53 | ```bash 54 | hatch version major 55 | ``` 56 | 57 | Your default Git text editor will open so you can add information about the release. 58 | 59 | When you push the tag on GitHub, the workflow will automatically publish it on PyPi and a GitHub release will be created as draft. 60 | 61 | ## Serve the documentation 62 | 63 | You can serve the Mkdocs documentation with: 64 | 65 | ```bash 66 | hatch run docs-serve 67 | ``` 68 | 69 | It'll automatically watch for changes in your code. 70 | 71 | ## License 72 | 73 | This project is licensed under the terms of the {{cookiecutter.open_source_license}}. 74 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/docs/index.md: -------------------------------------------------------------------------------- 1 | --8<-- "README.md" 2 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/docs/reference/{{cookiecutter.package_name}}.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | ::: {{ cookiecutter.package_name }} 4 | options: 5 | show_root_heading: false 6 | show_source: false 7 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: {{ cookiecutter.project_name }} 2 | site_description: {{ cookiecutter.project_short_description }} 3 | 4 | repo_url: {{ cookiecutter.repository_url }} 5 | repo_name: {{ cookiecutter.repository_name }} 6 | 7 | theme: 8 | name: material 9 | icon: 10 | logo: {{ cookiecutter.docs_icon }} 11 | palette: 12 | # Palette toggle for automatic mode 13 | - media: "(prefers-color-scheme)" 14 | toggle: 15 | icon: material/brightness-auto 16 | name: Switch to light mode 17 | 18 | # Palette toggle for light mode 19 | - media: "(prefers-color-scheme: light)" 20 | scheme: default 21 | primary: {{ cookiecutter.docs_color_primary }} 22 | accent: {{ cookiecutter.docs_color_accent }} 23 | toggle: 24 | icon: material/brightness-7 25 | name: Switch to dark mode 26 | 27 | # Palette toggle for dark mode 28 | - media: "(prefers-color-scheme: dark)" 29 | scheme: slate 30 | primary: {{ cookiecutter.docs_color_primary }} 31 | accent: {{ cookiecutter.docs_color_accent }} 32 | toggle: 33 | icon: material/brightness-4 34 | name: Switch to light mode 35 | 36 | markdown_extensions: 37 | - toc: 38 | permalink: true 39 | - pymdownx.highlight: 40 | anchor_linenums: true 41 | - pymdownx.tasklist: 42 | custom_checkbox: true 43 | - pymdownx.inlinehilite 44 | - pymdownx.snippets 45 | - pymdownx.superfences 46 | 47 | plugins: 48 | - search 49 | - mkdocstrings: 50 | handlers: 51 | python: 52 | import: 53 | - https://docs.python.org/{{ cookiecutter.python_version }}/objects.inv 54 | options: 55 | docstring_style: google 56 | 57 | watch: 58 | - docs 59 | - {{ cookiecutter.package_name }} 60 | 61 | nav: 62 | - About: index.md 63 | - Reference: 64 | - {{ cookiecutter.package_name }}: reference/{{ cookiecutter.package_name }}.md 65 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.ruff] 2 | target-version = "py{{ cookiecutter.python_version | replace(".", "") }}" 3 | 4 | [tool.ruff.lint] 5 | extend-select = ["I", "TRY", "UP"] 6 | 7 | [tool.mypy] 8 | warn_redundant_casts = true 9 | warn_unused_ignores = true 10 | disallow_any_generics = true 11 | check_untyped_defs = true 12 | no_implicit_reexport = true 13 | strict_equality = true 14 | disallow_untyped_defs = true 15 | 16 | [tool.pytest.ini_options] 17 | addopts = "--cov={{ cookiecutter.package_name }}/ --cov-report=term-missing" 18 | {% if cookiecutter.async == "asyncio" -%} 19 | asyncio_default_fixture_loop_scope = "function" 20 | asyncio_mode = "strict" 21 | {% elif cookiecutter.async == "anyio" -%} 22 | {%- endif %} 23 | 24 | [tool.hatch] 25 | 26 | [tool.hatch.metadata] 27 | allow-direct-references = true 28 | 29 | [tool.hatch.version] 30 | source = "regex_commit" 31 | commit_extra_args = ["-e"] 32 | path = "{{ cookiecutter.package_name }}/__init__.py" 33 | 34 | [tool.hatch.envs.default] 35 | installer = "uv" 36 | python = "{{ cookiecutter.python_version }}" 37 | dependencies = [ 38 | "mypy", 39 | "ruff", 40 | "pytest", 41 | "pytest-cov", 42 | "mkdocs-material", 43 | "mkdocstrings[python]", 44 | {% if cookiecutter.async == "asyncio" -%} 45 | "pytest-asyncio", 46 | {%- endif %} 47 | ] 48 | 49 | [tool.hatch.envs.default.scripts] 50 | test = "pytest" 51 | test-cov-xml = "pytest --cov-report=xml" 52 | lint = [ 53 | "ruff format .", 54 | "ruff check --fix .", 55 | "mypy {{ cookiecutter.package_name }}/", 56 | ] 57 | lint-check = [ 58 | "ruff format --check .", 59 | "ruff check .", 60 | "mypy {{ cookiecutter.package_name }}/", 61 | ] 62 | docs-serve = "mkdocs serve" 63 | docs-build = "mkdocs build" 64 | 65 | [build-system] 66 | requires = ["hatchling", "hatch-regex-commit"] 67 | build-backend = "hatchling.build" 68 | 69 | [project] 70 | name = "{{ cookiecutter.dist_name }}" 71 | authors = [ 72 | { name = "{{ cookiecutter.dist_name }}", email = "{{ cookiecutter.email }}" } 73 | ] 74 | description = "{{ cookiecutter.project_short_description }}" 75 | readme = "README.md" 76 | dynamic = ["version"] 77 | classifiers = [ 78 | "Programming Language :: Python :: 3 :: Only", 79 | "Programming Language :: Python :: {{ cookiecutter.python_version }}", 80 | ] 81 | requires-python = ">={{ cookiecutter.python_version }}" 82 | dependencies = [ 83 | {% if cookiecutter.async == "anyio" -%} 84 | "anyio", 85 | {%- endif %} 86 | ] 87 | 88 | [project.urls] 89 | Documentation = "{{ cookiecutter.docs_url }}" 90 | Source = "{{ cookiecutter.repository_url }}" 91 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankie567/cookiecutter-hipster-pypackage/f99966d68e1f21903415518bd68fa11acd97b511/{{cookiecutter.dist_name}}/tests/__init__.py -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/tests/conftest.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankie567/cookiecutter-hipster-pypackage/f99966d68e1f21903415518bd68fa11acd97b511/{{cookiecutter.dist_name}}/tests/conftest.py -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/tests/test_add.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from {{ cookiecutter.package_name }} import add 4 | 5 | 6 | @pytest.mark.parametrize( 7 | "a,b,result", 8 | [ 9 | (0, 0, 0), 10 | (1, 1, 2), 11 | (3, 2, 5), 12 | ], 13 | ) 14 | def test_add(a: int, b: int, result: int): 15 | assert add(a, b) == result 16 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/{{cookiecutter.package_name}}/__init__.py: -------------------------------------------------------------------------------- 1 | """{{cookiecutter.project_short_description}}""" 2 | 3 | __version__ = "0.0.0" 4 | 5 | 6 | def add(a: int, b: int) -> int: 7 | """ 8 | Add two integers. 9 | 10 | Args: 11 | a: 12 | The first operand. 13 | b: 14 | The second operand. 15 | 16 | Examples: 17 | Add two integers 18 | 19 | r = add(2, 3) 20 | print(r) # 5 21 | """ 22 | return a + b 23 | -------------------------------------------------------------------------------- /{{cookiecutter.dist_name}}/{{cookiecutter.package_name}}/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frankie567/cookiecutter-hipster-pypackage/f99966d68e1f21903415518bd68fa11acd97b511/{{cookiecutter.dist_name}}/{{cookiecutter.package_name}}/py.typed --------------------------------------------------------------------------------