├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── init.sh ├── release_message.sh ├── rename_project.sh ├── template.yml └── workflows │ ├── main.yml │ ├── release.yml │ └── rename_project.yml ├── .gitignore ├── ABOUT_THIS_TEMPLATE.md ├── CONTRIBUTING.md ├── Containerfile ├── HISTORY.md ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── docs └── index.md ├── mkdocs.yml ├── project_name ├── VERSION ├── __init__.py ├── __main__.py ├── base.py └── cli.py ├── requirements-test.txt ├── requirements.txt ├── setup.py └── tests ├── __init__.py ├── conftest.py └── test_base.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [rochacbruno] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug, help wanted 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Version [e.g. 22] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement, question 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Summary :memo: 2 | _Write an overview about it._ 3 | 4 | ### Details 5 | _Describe more what you did on changes._ 6 | 1. (...) 7 | 2. (...) 8 | 9 | ### Bugfixes :bug: (delete if dind't have any) 10 | - 11 | 12 | ### Checks 13 | - [ ] Closed #798 14 | - [ ] Tested Changes 15 | - [ ] Stakeholder Approval 16 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" -------------------------------------------------------------------------------- /.github/init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | overwrite_template_dir=0 3 | 4 | while getopts t:o flag 5 | do 6 | case "${flag}" in 7 | t) template=${OPTARG};; 8 | o) overwrite_template_dir=1;; 9 | esac 10 | done 11 | 12 | if [ -z "${template}" ]; then 13 | echo "Available templates: flask" 14 | read -p "Enter template name: " template 15 | fi 16 | 17 | repo_urlname=$(basename -s .git `git config --get remote.origin.url`) 18 | repo_name=$(basename -s .git `git config --get remote.origin.url` | tr '-' '_' | tr '[:upper:]' '[:lower:]') 19 | repo_owner=$(git config --get remote.origin.url | awk -F ':' '{print $2}' | awk -F '/' '{print $1}') 20 | echo "Repo name: ${repo_name}" 21 | echo "Repo owner: ${repo_owner}" 22 | echo "Repo urlname: ${repo_urlname}" 23 | 24 | if [ -f ".github/workflows/rename_project.yml" ]; then 25 | .github/rename_project.sh -a "${repo_owner}" -n "${repo_name}" -u "${repo_urlname}" -d "Awesome ${repo_name} created by ${repo_owner}" 26 | fi 27 | 28 | function download_template { 29 | rm -rf "${template_dir}" 30 | mkdir -p .github/templates 31 | git clone "${template_url}" "${template_dir}" 32 | } 33 | 34 | echo "Using template:${template}" 35 | template_url="https://github.com/rochacbruno/${template}-project-template" 36 | template_dir=".github/templates/${template}" 37 | if [ -d "${template_dir}" ]; then 38 | # Template directory already exists 39 | if [ "${overwrite_template_dir}" -eq 1 ]; then 40 | # user passed -o flag, delete and re-download 41 | echo "Overwriting ${template_dir}" 42 | download_template 43 | else 44 | # Ask user if they want to overwrite 45 | echo "Directory ${template_dir} already exists." 46 | read -p "Do you want to overwrite it? [y/N] " -n 1 -r 47 | echo 48 | if [[ $REPLY =~ ^[Yy]$ ]]; then 49 | echo "Overwriting ${template_dir}" 50 | download_template 51 | else 52 | # User decided not to overwrite 53 | echo "Using existing ${template_dir}" 54 | fi 55 | fi 56 | else 57 | # Template directory does not exist, download it 58 | echo "Downloading ${template_url}" 59 | download_template 60 | fi 61 | 62 | echo "Applying ${template} template to this project"} 63 | ./.github/templates/${template}/apply.sh -a "${repo_owner}" -n "${repo_name}" -u "${repo_urlname}" -d "Awesome ${repo_name} created by ${repo_owner}" 64 | 65 | # echo "Removing temporary template files" 66 | # rm -rf .github/templates/${template} 67 | 68 | echo "Done! review, commit and push the changes" 69 | -------------------------------------------------------------------------------- /.github/release_message.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | previous_tag=$(git tag --sort=-creatordate | sed -n 2p) 3 | git shortlog "${previous_tag}.." | sed 's/^./ &/' 4 | -------------------------------------------------------------------------------- /.github/rename_project.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | while getopts a:n:u:d: flag 3 | do 4 | case "${flag}" in 5 | a) author=${OPTARG};; 6 | n) name=${OPTARG};; 7 | u) urlname=${OPTARG};; 8 | d) description=${OPTARG};; 9 | esac 10 | done 11 | 12 | echo "Author: $author"; 13 | echo "Project Name: $name"; 14 | echo "Project URL name: $urlname"; 15 | echo "Description: $description"; 16 | 17 | echo "Renaming project..." 18 | 19 | original_author="author_name" 20 | original_name="project_name" 21 | original_urlname="project_urlname" 22 | original_description="project_description" 23 | # for filename in $(find . -name "*.*") 24 | for filename in $(git ls-files) 25 | do 26 | sed -i "s/$original_author/$author/g" $filename 27 | sed -i "s/$original_name/$name/g" $filename 28 | sed -i "s/$original_urlname/$urlname/g" $filename 29 | sed -i "s/$original_description/$description/g" $filename 30 | echo "Renamed $filename" 31 | done 32 | 33 | mv project_name $name 34 | 35 | # This command runs only once on GHA! 36 | rm -rf .github/template.yml 37 | -------------------------------------------------------------------------------- /.github/template.yml: -------------------------------------------------------------------------------- 1 | author: rochacbruno 2 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: [ main ] 10 | pull_request: 11 | branches: [ main ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | jobs: 17 | linter: 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | python-version: [3.9] 22 | os: [ubuntu-latest] 23 | runs-on: ${{ matrix.os }} 24 | steps: 25 | - uses: actions/checkout@v3 26 | - uses: actions/setup-python@v4 27 | with: 28 | python-version: ${{ matrix.python-version }} 29 | - name: Install project 30 | run: make install 31 | - name: Run linter 32 | run: make lint 33 | 34 | tests_linux: 35 | needs: linter 36 | strategy: 37 | fail-fast: false 38 | matrix: 39 | python-version: [3.9] 40 | os: [ubuntu-latest] 41 | runs-on: ${{ matrix.os }} 42 | steps: 43 | - uses: actions/checkout@v3 44 | - uses: actions/setup-python@v4 45 | with: 46 | python-version: ${{ matrix.python-version }} 47 | - name: Install project 48 | run: make install 49 | - name: Run tests 50 | run: make test 51 | - name: "Upload coverage to Codecov" 52 | uses: codecov/codecov-action@v3 53 | # with: 54 | # fail_ci_if_error: true 55 | 56 | tests_mac: 57 | needs: linter 58 | strategy: 59 | fail-fast: false 60 | matrix: 61 | python-version: [3.9] 62 | os: [macos-latest] 63 | runs-on: ${{ matrix.os }} 64 | steps: 65 | - uses: actions/checkout@v3 66 | - uses: actions/setup-python@v4 67 | with: 68 | python-version: ${{ matrix.python-version }} 69 | - name: Install project 70 | run: make install 71 | - name: Run tests 72 | run: make test 73 | 74 | tests_win: 75 | needs: linter 76 | strategy: 77 | fail-fast: false 78 | matrix: 79 | python-version: [3.9] 80 | os: [windows-latest] 81 | runs-on: ${{ matrix.os }} 82 | steps: 83 | - uses: actions/checkout@v3 84 | - uses: actions/setup-python@v4 85 | with: 86 | python-version: ${{ matrix.python-version }} 87 | - name: Install Pip 88 | run: pip install --user --upgrade pip 89 | - name: Install project 90 | run: pip install -e .[test] 91 | - name: run tests 92 | run: pytest -s -vvvv -l --tb=long tests 93 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Upload Python Package 2 | permissions: 3 | contents: write 4 | 5 | on: 6 | push: 7 | # Sequence of patterns matched against refs/tags 8 | tags: 9 | - '*' # Push events to matching v*, i.e. v1.0, v20.15.10 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | jobs: 15 | release: 16 | name: Create Release 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: write 20 | steps: 21 | - uses: actions/checkout@v3 22 | with: 23 | # by default, it uses a depth of 1 24 | # this fetches all history so that we can read each commit 25 | fetch-depth: 0 26 | - name: Generate Changelog 27 | run: .github/release_message.sh > release_message.md 28 | - name: Release 29 | uses: softprops/action-gh-release@v1 30 | with: 31 | body_path: release_message.md 32 | 33 | deploy: 34 | needs: release 35 | runs-on: ubuntu-latest 36 | steps: 37 | - uses: actions/checkout@v3 38 | - name: Set up Python 39 | uses: actions/setup-python@v4 40 | with: 41 | python-version: '3.x' 42 | - name: Install dependencies 43 | run: | 44 | python -m pip install --upgrade pip 45 | pip install setuptools wheel twine 46 | - name: Build and publish 47 | env: 48 | TWINE_USERNAME: __token__ 49 | TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} 50 | run: | 51 | python setup.py sdist bdist_wheel 52 | twine upload dist/* 53 | -------------------------------------------------------------------------------- /.github/workflows/rename_project.yml: -------------------------------------------------------------------------------- 1 | name: Rename the project from template 2 | 3 | on: [push] 4 | 5 | permissions: write-all 6 | 7 | jobs: 8 | rename-project: 9 | if: ${{ !contains (github.repository, '/python-project-template') }} 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | with: 14 | # by default, it uses a depth of 1 15 | # this fetches all history so that we can read each commit 16 | fetch-depth: 0 17 | ref: ${{ github.head_ref }} 18 | 19 | - run: echo "REPOSITORY_NAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}' | tr '-' '_' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV 20 | shell: bash 21 | 22 | - run: echo "REPOSITORY_URLNAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_ENV 23 | shell: bash 24 | 25 | - run: echo "REPOSITORY_OWNER=$(echo '${{ github.repository }}' | awk -F '/' '{print $1}')" >> $GITHUB_ENV 26 | shell: bash 27 | 28 | - name: Is this still a template 29 | id: is_template 30 | run: echo "::set-output name=is_template::$(ls .github/template.yml &> /dev/null && echo true || echo false)" 31 | 32 | - name: Rename the project 33 | if: steps.is_template.outputs.is_template == 'true' 34 | run: | 35 | echo "Renaming the project with -a(author) ${{ env.REPOSITORY_OWNER }} -n(name) ${{ env.REPOSITORY_NAME }} -u(urlname) ${{ env.REPOSITORY_URLNAME }}" 36 | .github/rename_project.sh -a ${{ env.REPOSITORY_OWNER }} -n ${{ env.REPOSITORY_NAME }} -u ${{ env.REPOSITORY_URLNAME }} -d "Awesome ${{ env.REPOSITORY_NAME }} created by ${{ env.REPOSITORY_OWNER }}" 37 | 38 | - uses: stefanzweifel/git-auto-commit-action@v4 39 | with: 40 | commit_message: "✅ Ready to clone and code." 41 | # commit_options: '--amend --no-edit' 42 | push_options: --force 43 | -------------------------------------------------------------------------------- /.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 | # Pyre type checker 129 | .pyre/ 130 | 131 | # templates 132 | .github/templates/* 133 | -------------------------------------------------------------------------------- /ABOUT_THIS_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # About this template 2 | 3 | Hi, I created this template to help you get started with a new project. 4 | 5 | I have created and maintained a number of python libraries, applications and 6 | frameworks and during those years I have learned a lot about how to create a 7 | project structure and how to structure a project to be as modular and simple 8 | as possible. 9 | 10 | Some decisions I have made while creating this template are: 11 | 12 | - Create a project structure that is as modular as possible. 13 | - Keep it simple and easy to maintain. 14 | - Allow for a lot of flexibility and customizability. 15 | - Low dependency (this template doesn't add dependencies) 16 | 17 | ## Structure 18 | 19 | Lets take a look at the structure of this template: 20 | 21 | ```text 22 | ├── Containerfile # The file to build a container using buildah or docker 23 | ├── CONTRIBUTING.md # Onboarding instructions for new contributors 24 | ├── docs # Documentation site (add more .md files here) 25 | │   └── index.md # The index page for the docs site 26 | ├── .github # Github metadata for repository 27 | │   ├── release_message.sh # A script to generate a release message 28 | │   └── workflows # The CI pipeline for Github Actions 29 | ├── .gitignore # A list of files to ignore when pushing to Github 30 | ├── HISTORY.md # Auto generated list of changes to the project 31 | ├── LICENSE # The license for the project 32 | ├── Makefile # A collection of utilities to manage the project 33 | ├── MANIFEST.in # A list of files to include in a package 34 | ├── mkdocs.yml # Configuration for documentation site 35 | ├── project_name # The main python package for the project 36 | │   ├── base.py # The base module for the project 37 | │   ├── __init__.py # This tells Python that this is a package 38 | │   ├── __main__.py # The entry point for the project 39 | │   └── VERSION # The version for the project is kept in a static file 40 | ├── README.md # The main readme for the project 41 | ├── setup.py # The setup.py file for installing and packaging the project 42 | ├── requirements.txt # An empty file to hold the requirements for the project 43 | ├── requirements-test.txt # List of requirements for testing and devlopment 44 | ├── setup.py # The setup.py file for installing and packaging the project 45 | └── tests # Unit tests for the project (add mote tests files here) 46 | ├── conftest.py # Configuration, hooks and fixtures for pytest 47 | ├── __init__.py # This tells Python that this is a test package 48 | └── test_base.py # The base test case for the project 49 | ``` 50 | 51 | ## FAQ 52 | 53 | Frequent asked questions. 54 | 55 | ### Why this template is not using [Poetry](https://python-poetry.org/) ? 56 | 57 | I really like Poetry and I think it is a great tool to manage your python projects, 58 | if you want to switch to poetry, you can run `make switch-to-poetry`. 59 | 60 | But for this template I wanted to keep it simple. 61 | 62 | Setuptools is the most simple and well supported way of packaging a Python project, 63 | it doesn't require extra dependencies and is the easiest way to install the project. 64 | 65 | Also, poetry doesn't have a good support for installing projects in development mode yet. 66 | 67 | ### Why the `requirements.txt` is empty ? 68 | 69 | This template is a low dependency project, so it doesn't have any extra dependencies. 70 | You can add new dependencies as you will or you can use the `make init` command to 71 | generate a `requirements.txt` file based on the template you choose `flask, fastapi, click etc`. 72 | 73 | ### Why there is a `requirements-test.txt` file ? 74 | 75 | This file lists all the requirements for testing and development, 76 | I think the development environment and testing environment should be as similar as possible. 77 | 78 | Except those tools that are up to the developer choice (like ipython, ipdb etc). 79 | 80 | ### Why the template doesn't have a `pyproject.toml` file ? 81 | 82 | It is possible to run `pip install https://github.com/name/repo/tarball/main` and 83 | have pip to download the package direcly from Git repo. 84 | 85 | For that to work you need to have a `setup.py` file, and `pyproject.toml` is not 86 | supported for that kind of installation. 87 | 88 | I think it is easier for example you want to install specific branch or tag you can 89 | do `pip install https://github.com/name/repo/tarball/{TAG|REVISON|COMMIT}` 90 | 91 | People automating CI for your project will be grateful for having a setup.py file 92 | 93 | ### Why isn't this template made as a cookiecutter template? 94 | 95 | I really like [cookiecutter](https://github.com/cookiecutter/cookiecutter) and it is a great way to create new projects, 96 | but for this template I wanted to use the Github `Use this template` button, 97 | to use this template doesn't require to install extra tooling such as cookiecutter. 98 | 99 | Just click on [Use this template](https://github.com/rochacbruno/python-project-template/generate) and you are good to go. 100 | 101 | The substituions are done using github actions and a simple sed script. 102 | 103 | ### Why `VERSION` is kept in a static plain text file? 104 | 105 | I used to have my version inside my main module in a `__version__` variable, then 106 | I had to do some tricks to read that version variable inside the setuptools 107 | `setup.py` file because that would be available only after the installation. 108 | 109 | I decided to keep the version in a static file because it is easier to read from 110 | wherever I want without the need to install the package. 111 | 112 | e.g: `cat project_name/VERSION` will get the project version without harming 113 | with module imports or anything else, it is useful for CI, logs and debugging. 114 | 115 | ### Why to include `tests`, `history` and `Containerfile` as part of the release? 116 | 117 | The `MANIFEST.in` file is used to include the files in the release, once the 118 | project is released to PyPI all the files listed on MANIFEST.in will be included 119 | even if the files are static or not related to Python. 120 | 121 | Some build systems such as RPM, DEB, AUR for some Linux distributions, and also 122 | internal repackaging systems tends to run the tests before the packaging is performed. 123 | 124 | The Containerfile can be useful to provide a safer execution environment for 125 | the project when running on a testing environment. 126 | 127 | I added those files to make it easier for packaging in different formats. 128 | 129 | ### Why conftest includes a go_to_tmpdir fixture? 130 | 131 | When your project deals with file system operations, it is a good idea to use 132 | a fixture to create a temporary directory and then remove it after the test. 133 | 134 | Before executing each test pytest will create a temporary directory and will 135 | change the working directory to that path and run the test. 136 | 137 | So the test can create temporary artifacts isolated from other tests. 138 | 139 | After the execution Pytest will remove the temporary directory. 140 | 141 | ### Why this template is not using [pre-commit](https://pre-commit.com/) ? 142 | 143 | pre-commit is an excellent tool to automate checks and formatting on your code. 144 | 145 | However I figured out that pre-commit adds extra dependency and it an entry barrier 146 | for new contributors. 147 | 148 | Having the linting, checks and formatting as simple commands on the [Makefile](Makefile) 149 | makes it easier to undestand and change. 150 | 151 | Once the project is bigger and complex, having pre-commit as a dependency can be a good idea. 152 | 153 | ### Why the CLI is not using click? 154 | 155 | I wanted to provide a simple template for a CLI application on the project main entry point 156 | click and typer are great alternatives but are external dependencies and this template 157 | doesn't add dependencies besides those used for development. 158 | 159 | ### Why this doesn't provide a full example of application using Flask or Django? 160 | 161 | as I said before, I want it to be simple and multipurpose, so I decided to not include 162 | external dependencies and programming design decisions. 163 | 164 | It is up to you to decide if you want to use Flask or Django and to create your application 165 | the way you think is best. 166 | 167 | This template provides utilities in the Makefile to make it easier to you can run: 168 | 169 | ```bash 170 | $ make init 171 | Which template do you want to apply? [flask, fastapi, click, typer]? > flask 172 | Generating a new project with Flask ... 173 | ``` 174 | 175 | Then the above will download the Flask template and apply it to the project. 176 | 177 | ## The Makefile 178 | 179 | All the utilities for the template and project are on the Makefile 180 | 181 | ```bash 182 | ❯ make 183 | Usage: make 184 | 185 | Targets: 186 | help: ## Show the help. 187 | install: ## Install the project in dev mode. 188 | fmt: ## Format code using black & isort. 189 | lint: ## Run pep8, black, mypy linters. 190 | test: lint ## Run tests and generate coverage report. 191 | watch: ## Run tests on every change. 192 | clean: ## Clean unused files. 193 | virtualenv: ## Create a virtual environment. 194 | release: ## Create a new tag for release. 195 | docs: ## Build the documentation. 196 | switch-to-poetry: ## Switch to poetry package manager. 197 | init: ## Initialize the project based on an application template. 198 | ``` 199 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to develop on this project 2 | 3 | project_name welcomes contributions from the community. 4 | 5 | **You need PYTHON3!** 6 | 7 | This instructions are for linux base systems. (Linux, MacOS, BSD, etc.) 8 | ## Setting up your own fork of this repo. 9 | 10 | - On github interface click on `Fork` button. 11 | - Clone your fork of this repo. `git clone git@github.com:YOUR_GIT_USERNAME/project_urlname.git` 12 | - Enter the directory `cd project_urlname` 13 | - Add upstream repo `git remote add upstream https://github.com/author_name/project_urlname` 14 | 15 | ## Setting up your own virtual environment 16 | 17 | Run `make virtualenv` to create a virtual environment. 18 | then activate it with `source .venv/bin/activate`. 19 | 20 | ## Install the project in develop mode 21 | 22 | Run `make install` to install the project in develop mode. 23 | 24 | ## Run the tests to ensure everything is working 25 | 26 | Run `make test` to run the tests. 27 | 28 | ## Create a new branch to work on your contribution 29 | 30 | Run `git checkout -b my_contribution` 31 | 32 | ## Make your changes 33 | 34 | Edit the files using your preferred editor. (we recommend VIM or VSCode) 35 | 36 | ## Format the code 37 | 38 | Run `make fmt` to format the code. 39 | 40 | ## Run the linter 41 | 42 | Run `make lint` to run the linter. 43 | 44 | ## Test your changes 45 | 46 | Run `make test` to run the tests. 47 | 48 | Ensure code coverage report shows `100%` coverage, add tests to your PR. 49 | 50 | ## Build the docs locally 51 | 52 | Run `make docs` to build the docs. 53 | 54 | Ensure your new changes are documented. 55 | 56 | ## Commit your changes 57 | 58 | This project uses [conventional git commit messages](https://www.conventionalcommits.org/en/v1.0.0/). 59 | 60 | Example: `fix(package): update setup.py arguments 🎉` (emojis are fine too) 61 | 62 | ## Push your changes to your fork 63 | 64 | Run `git push origin my_contribution` 65 | 66 | ## Submit a pull request 67 | 68 | On github interface, click on `Pull Request` button. 69 | 70 | Wait CI to run and one of the developers will review your PR. 71 | ## Makefile utilities 72 | 73 | This project comes with a `Makefile` that contains a number of useful utility. 74 | 75 | ```bash 76 | ❯ make 77 | Usage: make 78 | 79 | Targets: 80 | help: ## Show the help. 81 | install: ## Install the project in dev mode. 82 | fmt: ## Format code using black & isort. 83 | lint: ## Run pep8, black, mypy linters. 84 | test: lint ## Run tests and generate coverage report. 85 | watch: ## Run tests on every change. 86 | clean: ## Clean unused files. 87 | virtualenv: ## Create a virtual environment. 88 | release: ## Create a new tag for release. 89 | docs: ## Build the documentation. 90 | switch-to-poetry: ## Switch to poetry package manager. 91 | init: ## Initialize the project based on an application template. 92 | ``` 93 | 94 | ## Making a new release 95 | 96 | This project uses [semantic versioning](https://semver.org/) and tags releases with `X.Y.Z` 97 | Every time a new tag is created and pushed to the remote repo, github actions will 98 | automatically create a new release on github and trigger a release on PyPI. 99 | 100 | For this to work you need to setup a secret called `PIPY_API_TOKEN` on the project settings>secrets, 101 | this token can be generated on [pypi.org](https://pypi.org/account/). 102 | 103 | To trigger a new release all you need to do is. 104 | 105 | 1. If you have changes to add to the repo 106 | * Make your changes following the steps described above. 107 | * Commit your changes following the [conventional git commit messages](https://www.conventionalcommits.org/en/v1.0.0/). 108 | 2. Run the tests to ensure everything is working. 109 | 4. Run `make release` to create a new tag and push it to the remote repo. 110 | 111 | the `make release` will ask you the version number to create the tag, ex: type `0.1.1` when you are asked. 112 | 113 | > **CAUTION**: The make release will change local changelog files and commit all the unstaged changes you have. 114 | -------------------------------------------------------------------------------- /Containerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-slim 2 | COPY . /app 3 | WORKDIR /app 4 | RUN pip install . 5 | CMD ["project_name"] 6 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | 5 | 0.1.2 (2021-08-14) 6 | ------------------ 7 | - Fix release, README and windows CI. [Bruno Rocha] 8 | - Release: version 0.1.0. [Bruno Rocha] 9 | 10 | 11 | 0.1.0 (2021-08-14) 12 | ------------------ 13 | - Add release command. [Bruno Rocha] 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include HISTORY.md 3 | include Containerfile 4 | graft tests 5 | graft project_name 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .ONESHELL: 2 | ENV_PREFIX=$(shell python -c "if __import__('pathlib').Path('.venv/bin/pip').exists(): print('.venv/bin/')") 3 | USING_POETRY=$(shell grep "tool.poetry" pyproject.toml && echo "yes") 4 | 5 | .PHONY: help 6 | help: ## Show the help. 7 | @echo "Usage: make " 8 | @echo "" 9 | @echo "Targets:" 10 | @fgrep "##" Makefile | fgrep -v fgrep 11 | 12 | 13 | .PHONY: show 14 | show: ## Show the current environment. 15 | @echo "Current environment:" 16 | @if [ "$(USING_POETRY)" ]; then poetry env info && exit; fi 17 | @echo "Running using $(ENV_PREFIX)" 18 | @$(ENV_PREFIX)python -V 19 | @$(ENV_PREFIX)python -m site 20 | 21 | .PHONY: install 22 | install: ## Install the project in dev mode. 23 | @if [ "$(USING_POETRY)" ]; then poetry install && exit; fi 24 | @echo "Don't forget to run 'make virtualenv' if you got errors." 25 | $(ENV_PREFIX)pip install -e .[test] 26 | 27 | .PHONY: fmt 28 | fmt: ## Format code using black & isort. 29 | $(ENV_PREFIX)isort project_name/ 30 | $(ENV_PREFIX)black -l 79 project_name/ 31 | $(ENV_PREFIX)black -l 79 tests/ 32 | 33 | .PHONY: lint 34 | lint: ## Run pep8, black, mypy linters. 35 | $(ENV_PREFIX)flake8 project_name/ 36 | $(ENV_PREFIX)black -l 79 --check project_name/ 37 | $(ENV_PREFIX)black -l 79 --check tests/ 38 | $(ENV_PREFIX)mypy --ignore-missing-imports project_name/ 39 | 40 | .PHONY: test 41 | test: lint ## Run tests and generate coverage report. 42 | $(ENV_PREFIX)pytest -v --cov-config .coveragerc --cov=project_name -l --tb=short --maxfail=1 tests/ 43 | $(ENV_PREFIX)coverage xml 44 | $(ENV_PREFIX)coverage html 45 | 46 | .PHONY: watch 47 | watch: ## Run tests on every change. 48 | ls **/**.py | entr $(ENV_PREFIX)pytest -s -vvv -l --tb=long --maxfail=1 tests/ 49 | 50 | .PHONY: clean 51 | clean: ## Clean unused files. 52 | @find ./ -name '*.pyc' -exec rm -f {} \; 53 | @find ./ -name '__pycache__' -exec rm -rf {} \; 54 | @find ./ -name 'Thumbs.db' -exec rm -f {} \; 55 | @find ./ -name '*~' -exec rm -f {} \; 56 | @rm -rf .cache 57 | @rm -rf .pytest_cache 58 | @rm -rf .mypy_cache 59 | @rm -rf build 60 | @rm -rf dist 61 | @rm -rf *.egg-info 62 | @rm -rf htmlcov 63 | @rm -rf .tox/ 64 | @rm -rf docs/_build 65 | 66 | .PHONY: virtualenv 67 | virtualenv: ## Create a virtual environment. 68 | @if [ "$(USING_POETRY)" ]; then poetry install && exit; fi 69 | @echo "creating virtualenv ..." 70 | @rm -rf .venv 71 | @python3 -m venv .venv 72 | @./.venv/bin/pip install -U pip 73 | @./.venv/bin/pip install -e .[test] 74 | @echo 75 | @echo "!!! Please run 'source .venv/bin/activate' to enable the environment !!!" 76 | 77 | .PHONY: release 78 | release: ## Create a new tag for release. 79 | @echo "WARNING: This operation will create s version tag and push to github" 80 | @read -p "Version? (provide the next x.y.z semver) : " TAG 81 | @echo "$${TAG}" > project_name/VERSION 82 | @$(ENV_PREFIX)gitchangelog > HISTORY.md 83 | @git add project_name/VERSION HISTORY.md 84 | @git commit -m "release: version $${TAG} 🚀" 85 | @echo "creating git tag : $${TAG}" 86 | @git tag $${TAG} 87 | @git push -u origin HEAD --tags 88 | @echo "Github Actions will detect the new tag and release the new version." 89 | 90 | .PHONY: docs 91 | docs: ## Build the documentation. 92 | @echo "building documentation ..." 93 | @$(ENV_PREFIX)mkdocs build 94 | URL="site/index.html"; xdg-open $$URL || sensible-browser $$URL || x-www-browser $$URL || gnome-open $$URL || open $$URL 95 | 96 | .PHONY: switch-to-poetry 97 | switch-to-poetry: ## Switch to poetry package manager. 98 | @echo "Switching to poetry ..." 99 | @if ! poetry --version > /dev/null; then echo 'poetry is required, install from https://python-poetry.org/'; exit 1; fi 100 | @rm -rf .venv 101 | @poetry init --no-interaction --name=a_flask_test --author=rochacbruno 102 | @echo "" >> pyproject.toml 103 | @echo "[tool.poetry.scripts]" >> pyproject.toml 104 | @echo "project_name = 'project_name.__main__:main'" >> pyproject.toml 105 | @cat requirements.txt | while read in; do poetry add --no-interaction "$${in}"; done 106 | @cat requirements-test.txt | while read in; do poetry add --no-interaction "$${in}" --dev; done 107 | @poetry install --no-interaction 108 | @mkdir -p .github/backup 109 | @mv requirements* .github/backup 110 | @mv setup.py .github/backup 111 | @echo "You have switched to https://python-poetry.org/ package manager." 112 | @echo "Please run 'poetry shell' or 'poetry run project_name'" 113 | 114 | .PHONY: init 115 | init: ## Initialize the project based on an application template. 116 | @./.github/init.sh 117 | 118 | 119 | # This project has been generated from rochacbruno/python-project-template 120 | # __author__ = 'rochacbruno' 121 | # __repo__ = https://github.com/rochacbruno/python-project-template 122 | # __sponsor__ = https://github.com/sponsors/rochacbruno/ 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!IMPORTANT] 2 | > This template is **archived**. 3 | > UV can now [generate a sample project](https://docs.astral.sh/uv/guides/projects/#creating-a-new-project) 4 | > I recommend using **UV** to bootstrap your peojects. 5 | > [Copier](https://github.com/copier-org/copier) is a tools that can bootstrap projects from templates. 6 | 7 | 8 | --- 9 | 10 | 11 | # Python Project Template 12 | 13 | A low dependency and really simple to start project template for Python Projects. 14 | 15 | See also 16 | - [Flask-Project-Template](https://github.com/rochacbruno/flask-project-template/) for a full feature Flask project including database, API, admin interface, etc. 17 | - [FastAPI-Project-Template](https://github.com/rochacbruno/fastapi-project-template/) The base to start an openapi project featuring: SQLModel, Typer, FastAPI, JWT Token Auth, Interactive Shell, Management Commands. 18 | 19 | ### HOW TO USE THIS TEMPLATE 20 | 21 | > **DO NOT FORK** this is meant to be used from **[Use this template](https://github.com/rochacbruno/python-project-template/generate)** feature. 22 | 23 | 1. Click on **[Use this template](https://github.com/rochacbruno/python-project-template/generate)** 24 | 3. Give a name to your project 25 | (e.g. `my_awesome_project` recommendation is to use all lowercase and underscores separation for repo names.) 26 | 3. Wait until the first run of CI finishes 27 | (Github Actions will process the template and commit to your new repo) 28 | 4. If you want [codecov](https://about.codecov.io/sign-up/) Reports and Automatic Release to [PyPI](https://pypi.org) 29 | On the new repository `settings->secrets` add your `PYPI_API_TOKEN` and `CODECOV_TOKEN` (get the tokens on respective websites) 30 | 4. Read the file [CONTRIBUTING.md](CONTRIBUTING.md) 31 | 5. Then clone your new project and happy coding! 32 | 33 | > **NOTE**: **WAIT** until first CI run on github actions before cloning your new project. 34 | 35 | ### What is included on this template? 36 | 37 | - 🖼️ Templates for starting multiple application types: 38 | * **Basic low dependency** Python program (default) [use this template](https://github.com/rochacbruno/python-project-template/generate) 39 | * **Flask** with database, admin interface, restapi and authentication [use this template](https://github.com/rochacbruno/flask-project-template/generate). 40 | **or Run `make init` after cloning to generate a new project based on a template.** 41 | - 📦 A basic [setup.py](setup.py) file to provide installation, packaging and distribution for your project. 42 | Template uses setuptools because it's the de-facto standard for Python packages, you can run `make switch-to-poetry` later if you want. 43 | - 🤖 A [Makefile](Makefile) with the most useful commands to install, test, lint, format and release your project. 44 | - 📃 Documentation structure using [mkdocs](http://www.mkdocs.org) 45 | - 💬 Auto generation of change log using **gitchangelog** to keep a HISTORY.md file automatically based on your commit history on every release. 46 | - 🐋 A simple [Containerfile](Containerfile) to build a container image for your project. 47 | `Containerfile` is a more open standard for building container images than Dockerfile, you can use buildah or docker with this file. 48 | - 🧪 Testing structure using [pytest](https://docs.pytest.org/en/latest/) 49 | - ✅ Code linting using [flake8](https://flake8.pycqa.org/en/latest/) 50 | - 📊 Code coverage reports using [codecov](https://about.codecov.io/sign-up/) 51 | - 🛳️ Automatic release to [PyPI](https://pypi.org) using [twine](https://twine.readthedocs.io/en/latest/) and github actions. 52 | - 🎯 Entry points to execute your program using `python -m ` or `$ project_name` with basic CLI argument parsing. 53 | - 🔄 Continuous integration using [Github Actions](.github/workflows/) with jobs to lint, test and release your project on Linux, Mac and Windows environments. 54 | 55 | > Curious about architectural decisions on this template? read [ABOUT_THIS_TEMPLATE.md](ABOUT_THIS_TEMPLATE.md) 56 | > If you want to contribute to this template please open an [issue](https://github.com/rochacbruno/python-project-template/issues) or fork and send a PULL REQUEST. 57 | 58 | [❤️ Sponsor this project](https://github.com/sponsors/rochacbruno/) 59 | 60 | 61 | 62 | --- 63 | # project_name 64 | 65 | [![codecov](https://codecov.io/gh/author_name/project_urlname/branch/main/graph/badge.svg?token=project_urlname_token_here)](https://codecov.io/gh/author_name/project_urlname) 66 | [![CI](https://github.com/author_name/project_urlname/actions/workflows/main.yml/badge.svg)](https://github.com/author_name/project_urlname/actions/workflows/main.yml) 67 | 68 | project_description 69 | 70 | ## Install it from PyPI 71 | 72 | ```bash 73 | pip install project_name 74 | ``` 75 | 76 | ## Usage 77 | 78 | ```py 79 | from project_name import BaseClass 80 | from project_name import base_function 81 | 82 | BaseClass().base_method() 83 | base_function() 84 | ``` 85 | 86 | ```bash 87 | $ python -m project_name 88 | #or 89 | $ project_name 90 | ``` 91 | 92 | ## Development 93 | 94 | Read the [CONTRIBUTING.md](CONTRIBUTING.md) file. 95 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Welcome to MkDocs 2 | 3 | For full documentation visit [mkdocs.org](https://www.mkdocs.org). 4 | 5 | ## Commands 6 | 7 | * `mkdocs new [dir-name]` - Create a new project. 8 | * `mkdocs serve` - Start the live-reloading docs server. 9 | * `mkdocs build` - Build the documentation site. 10 | * `mkdocs -h` - Print help message and exit. 11 | 12 | ## Project layout 13 | 14 | mkdocs.yml # The configuration file. 15 | docs/ 16 | index.md # The documentation homepage. 17 | ... # Other markdown pages, images and other files. 18 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: project_name 2 | theme: readthedocs 3 | -------------------------------------------------------------------------------- /project_name/VERSION: -------------------------------------------------------------------------------- 1 | 0.1.0 2 | -------------------------------------------------------------------------------- /project_name/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochacbruno/python-project-template/943695e4e1bf137ab1311939d604018bc0a348b4/project_name/__init__.py -------------------------------------------------------------------------------- /project_name/__main__.py: -------------------------------------------------------------------------------- 1 | """Entry point for project_name.""" 2 | 3 | from project_name.cli import main # pragma: no cover 4 | 5 | if __name__ == "__main__": # pragma: no cover 6 | main() 7 | -------------------------------------------------------------------------------- /project_name/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | project_name base module. 3 | 4 | This is the principal module of the project_name project. 5 | here you put your main classes and objects. 6 | 7 | Be creative! do whatever you want! 8 | 9 | If you want to replace this with a Flask application run: 10 | 11 | $ make init 12 | 13 | and then choose `flask` as template. 14 | """ 15 | 16 | # example constant variable 17 | NAME = "project_name" 18 | -------------------------------------------------------------------------------- /project_name/cli.py: -------------------------------------------------------------------------------- 1 | """CLI interface for project_name project. 2 | 3 | Be creative! do whatever you want! 4 | 5 | - Install click or typer and create a CLI app 6 | - Use builtin argparse 7 | - Start a web application 8 | - Import things from your .base module 9 | """ 10 | 11 | 12 | def main(): # pragma: no cover 13 | """ 14 | The main function executes on commands: 15 | `python -m project_name` and `$ project_name `. 16 | 17 | This is your program's entry point. 18 | 19 | You can change this function to do whatever you want. 20 | Examples: 21 | * Run a test suite 22 | * Run a server 23 | * Do some other stuff 24 | * Run a command line application (Click, Typer, ArgParse) 25 | * List all available tasks 26 | * Run an application (Flask, FastAPI, Django, etc.) 27 | """ 28 | print("This will do something") 29 | -------------------------------------------------------------------------------- /requirements-test.txt: -------------------------------------------------------------------------------- 1 | # These requirements are for development and testing only, not for production. 2 | pytest 3 | coverage 4 | flake8 5 | black 6 | isort 7 | pytest-cov 8 | mypy 9 | gitchangelog 10 | mkdocs 11 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # This template is a low-dependency template. 2 | # By default there is no requirements added here. 3 | # Add the requirements you need to this file. 4 | # or run `make init` to create this file automatically based on the template. 5 | # You can also run `make switch-to-poetry` to use the poetry package manager. 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """Python setup.py for project_name package""" 2 | import io 3 | import os 4 | from setuptools import find_packages, setup 5 | 6 | 7 | def read(*paths, **kwargs): 8 | """Read the contents of a text file safely. 9 | >>> read("project_name", "VERSION") 10 | '0.1.0' 11 | >>> read("README.md") 12 | ... 13 | """ 14 | 15 | content = "" 16 | with io.open( 17 | os.path.join(os.path.dirname(__file__), *paths), 18 | encoding=kwargs.get("encoding", "utf8"), 19 | ) as open_file: 20 | content = open_file.read().strip() 21 | return content 22 | 23 | 24 | def read_requirements(path): 25 | return [ 26 | line.strip() 27 | for line in read(path).split("\n") 28 | if not line.startswith(('"', "#", "-", "git+")) 29 | ] 30 | 31 | 32 | setup( 33 | name="project_name", 34 | version=read("project_name", "VERSION"), 35 | description="project_description", 36 | url="https://github.com/author_name/project_urlname/", 37 | long_description=read("README.md"), 38 | long_description_content_type="text/markdown", 39 | author="author_name", 40 | packages=find_packages(exclude=["tests", ".github"]), 41 | install_requires=read_requirements("requirements.txt"), 42 | entry_points={ 43 | "console_scripts": ["project_name = project_name.__main__:main"] 44 | }, 45 | extras_require={"test": read_requirements("requirements-test.txt")}, 46 | ) 47 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochacbruno/python-project-template/943695e4e1bf137ab1311939d604018bc0a348b4/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pytest 3 | 4 | 5 | # each test runs on cwd to its temp dir 6 | @pytest.fixture(autouse=True) 7 | def go_to_tmpdir(request): 8 | # Get the fixture dynamically by its name. 9 | tmpdir = request.getfixturevalue("tmpdir") 10 | # ensure local test created packages can be imported 11 | sys.path.insert(0, str(tmpdir)) 12 | # Chdir only for the duration of the test. 13 | with tmpdir.as_cwd(): 14 | yield 15 | -------------------------------------------------------------------------------- /tests/test_base.py: -------------------------------------------------------------------------------- 1 | from project_name.base import NAME 2 | 3 | 4 | def test_base(): 5 | assert NAME == "project_name" 6 | --------------------------------------------------------------------------------