initial commit
Some checks are pending
CI / tests_mac (macos-latest, 3.9) (push) Blocked by required conditions
CI / tests_win (windows-latest, 3.9) (push) Blocked by required conditions
CI / linter (ubuntu-latest, 3.9) (push) Successful in 3m30s
Rename the project from template / rename-project (push) Successful in 15s
CI / tests_linux (ubuntu-latest, 3.9) (push) Successful in 42s

This commit is contained in:
Simon Diesenreiter 2024-11-09 08:01:27 -08:00
parent 87d31059ad
commit 0e9b465413
42 changed files with 1403 additions and 37 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: [rochacbruno]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

31
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,31 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug, help wanted
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement, question
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

15
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,15 @@
### Summary :memo:
_Write an overview about it._
### Details
_Describe more what you did on changes._
1. (...)
2. (...)
### Bugfixes :bug: (delete if dind't have any)
-
### Checks
- [ ] Closed #798
- [ ] Tested Changes
- [ ] Stakeholder Approval

6
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "gitea-actions"
directory: "/"
schedule:
interval: "weekly"

68
.github/init.sh vendored Executable file
View File

@ -0,0 +1,68 @@
#!/usr/bin/env bash
overwrite_template_dir=0
while getopts t:o flag
do
case "${flag}" in
t) template=${OPTARG};;
o) overwrite_template_dir=1;;
esac
done
if [ -z "${template}" ]; then
echo "Available templates: flask"
read -p "Enter template name: " template
fi
repo_urlname=$(basename -s .git `git config --get remote.origin.url`)
repo_name=$(basename -s .git `git config --get remote.origin.url` | tr '-' '_' | tr '[:upper:]' '[:lower:]')
repo_owner=$(git config --get remote.origin.url | awk -F ':' '{print $2}' | awk -F '/' '{print $1}')
echo "Repo name: ${repo_name}"
echo "Repo owner: ${repo_owner}"
echo "Repo urlname: ${repo_urlname}"
if [ -f ".gitea/workflows/rename_project.yml" ]; then
.gitea/rename_project.sh -a "${repo_owner}" -n "${repo_name}" -u "${repo_urlname}" -d "Awesome ${repo_name} created by ${repo_owner}"
fi
function download_template {
rm -rf "${template_dir}"
mkdir -p .gitea/templates
git clone "${template_url}" "${template_dir}"
}
echo "Using template:${template}"
template_url="https://github.com/rochacbruno/${template}-project-template"
template_dir=".gitea/templates/${template}"
if [ -d "${template_dir}" ]; then
# Template directory already exists
if [ "${overwrite_template_dir}" -eq 1 ]; then
# user passed -o flag, delete and re-download
echo "Overwriting ${template_dir}"
download_template
else
# Ask user if they want to overwrite
echo "Directory ${template_dir} already exists."
read -p "Do you want to overwrite it? [y/N] " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Overwriting ${template_dir}"
download_template
else
# User decided not to overwrite
echo "Using existing ${template_dir}"
fi
fi
else
# Template directory does not exist, download it
echo "Downloading ${template_url}"
download_template
fi
echo "Applying ${template} template to this project"}
./.gitea/templates/${template}/apply.sh -a "${repo_owner}" -n "${repo_name}" -u "${repo_urlname}" -d "Awesome ${repo_name} created by ${repo_owner}"
# echo "Removing temporary template files"
# rm -rf .gitea/templates/${template}
echo "Done! review, commit and push the changes"

3
.github/release_message.sh vendored Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env bash
previous_tag=$(git tag --sort=-creatordate | sed -n 2p)
git shortlog "${previous_tag}.." | sed 's/^./ &/'

36
.github/rename_project.sh vendored Executable file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env bash
while getopts a:n:u:d: flag
do
case "${flag}" in
a) author=${OPTARG};;
n) name=${OPTARG};;
u) urlname=${OPTARG};;
d) description=${OPTARG};;
esac
done
echo "Author: $author";
echo "Project Name: $name";
echo "Project URL name: $urlname";
echo "Description: $description";
echo "Renaming project..."
original_author="author_name"
original_name="project_name"
original_urlname="project_urlname"
original_description="project_description"
# for filename in $(find . -name "*.*")
for filename in $(git ls-files)
do
sed -i "s/$original_author/$author/g" $filename
sed -i "s/$original_name/$name/g" $filename
sed -i "s/$original_urlname/$urlname/g" $filename
sed -i "s/$original_description/$description/g" $filename
echo "Renamed $filename"
done
mv project_name $name
# This command runs only once on GHA!
rm -rf .gitea/template.yml

1
.github/template.yml vendored Normal file
View File

@ -0,0 +1 @@
author: rochacbruno

92
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,92 @@
# This is a basic workflow to help you get started with Actions
name: CI
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [ main ]
pull_request:
branches: [ main ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
linter:
strategy:
fail-fast: false
matrix:
python-version: [3.9]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install project
run: make install
- name: Run linter
run: make lint
tests_linux:
needs: linter
strategy:
fail-fast: false
matrix:
python-version: [3.9]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install project
run: make install
- name: Run tests
run: make test
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v3
# with:
# fail_ci_if_error: true
tests_mac:
needs: linter
strategy:
fail-fast: false
matrix:
python-version: [3.9]
os: [macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install project
run: make install
- name: Run tests
run: make test
tests_win:
needs: linter
strategy:
fail-fast: false
matrix:
python-version: [3.9]
os: [windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install Pip
run: pip install --user --upgrade pip
- name: Install project
run: pip install -e .[test]
- name: run tests
run: pytest -s -vvvv -l --tb=long tests

52
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,52 @@
name: Upload Python Package
permissions:
contents: write
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- '*' # Push events to matching v*, i.e. v1.0, v20.15.10
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
release:
name: Create Release
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v3
with:
# by default, it uses a depth of 1
# this fetches all history so that we can read each commit
fetch-depth: 0
- name: Generate Changelog
run: .gitea/release_message.sh > release_message.md
- name: Release
uses: softprops/action-gh-release@v1
with:
body_path: release_message.md
deploy:
needs: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*

42
.github/workflows/rename_project.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: Rename the project from template
on: [push]
permissions: write-all
jobs:
rename-project:
if: ${{ !contains (github.repository, '/python-project-template') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
# by default, it uses a depth of 1
# this fetches all history so that we can read each commit
fetch-depth: 0
ref: ${{ github.head_ref }}
- run: echo "REPOSITORY_NAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}' | tr '-' '_' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
shell: bash
- run: echo "REPOSITORY_URLNAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_ENV
shell: bash
- run: echo "REPOSITORY_OWNER=$(echo '${{ github.repository }}' | awk -F '/' '{print $1}')" >> $GITHUB_ENV
shell: bash
- name: Is this still a template
id: is_template
run: echo "::set-output name=is_template::$(ls .gitea/template.yml &> /dev/null && echo true || echo false)"
- name: Rename the project
if: steps.is_template.outputs.is_template == 'true'
run: |
echo "Renaming the project with -a(author) ${{ env.REPOSITORY_OWNER }} -n(name) ${{ env.REPOSITORY_NAME }} -u(urlname) ${{ env.REPOSITORY_URLNAME }}"
.gitea/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 }}"
- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "✅ Ready to clone and code."
# commit_options: '--amend --no-edit'
push_options: --force

40
.gitignore vendored
View File

@ -1,4 +1,3 @@
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
@ -21,6 +20,7 @@ parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
@ -50,7 +50,6 @@ coverage.xml
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
@ -73,7 +72,6 @@ instance/
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
@ -84,9 +82,7 @@ profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
@ -95,22 +91,7 @@ ipython_config.py
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
@ -147,16 +128,5 @@ dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# templates
.gitea/templates/*

198
ABOUT_THIS_TEMPLATE.md Normal file
View File

@ -0,0 +1,198 @@
# About this template
Hi, I created this template to help you get started with a new project.
I have created and maintained a number of python libraries, applications and
frameworks and during those years I have learned a lot about how to create a
project structure and how to structure a project to be as modular and simple
as possible.
Some decisions I have made while creating this template are:
- Create a project structure that is as modular as possible.
- Keep it simple and easy to maintain.
- Allow for a lot of flexibility and customizability.
- Low dependency (this template doesn't add dependencies)
## Structure
Lets take a look at the structure of this template:
```text
├── Containerfile # The file to build a container using buildah or docker
├── CONTRIBUTING.md # Onboarding instructions for new contributors
├── docs # Documentation site (add more .md files here)
│   └── index.md # The index page for the docs site
├── .gitea # Gitea metadata for repository
│   ├── release_message.sh # A script to generate a release message
│   └── workflows # The CI pipeline for Gitea Actions
├── .gitignore # A list of files to ignore when pushing to Gitea
├── HISTORY.md # Auto generated list of changes to the project
├── LICENSE # The license for the project
├── Makefile # A collection of utilities to manage the project
├── MANIFEST.in # A list of files to include in a package
├── mkdocs.yml # Configuration for documentation site
├── project_name # The main python package for the project
│   ├── base.py # The base module for the project
│   ├── __init__.py # This tells Python that this is a package
│   ├── __main__.py # The entry point for the project
│   └── VERSION # The version for the project is kept in a static file
├── README.md # The main readme for the project
├── setup.py # The setup.py file for installing and packaging the project
├── requirements.txt # An empty file to hold the requirements for the project
├── requirements-test.txt # List of requirements for testing and devlopment
├── setup.py # The setup.py file for installing and packaging the project
└── tests # Unit tests for the project (add mote tests files here)
├── conftest.py # Configuration, hooks and fixtures for pytest
├── __init__.py # This tells Python that this is a test package
└── test_base.py # The base test case for the project
```
## FAQ
Frequent asked questions.
### Why this template is not using [Poetry](https://python-poetry.org/) ?
I really like Poetry and I think it is a great tool to manage your python projects,
if you want to switch to poetry, you can run `make switch-to-poetry`.
But for this template I wanted to keep it simple.
Setuptools is the most simple and well supported way of packaging a Python project,
it doesn't require extra dependencies and is the easiest way to install the project.
Also, poetry doesn't have a good support for installing projects in development mode yet.
### Why the `requirements.txt` is empty ?
This template is a low dependency project, so it doesn't have any extra dependencies.
You can add new dependencies as you will or you can use the `make init` command to
generate a `requirements.txt` file based on the template you choose `flask, fastapi, click etc`.
### Why there is a `requirements-test.txt` file ?
This file lists all the requirements for testing and development,
I think the development environment and testing environment should be as similar as possible.
Except those tools that are up to the developer choice (like ipython, ipdb etc).
### Why the template doesn't have a `pyproject.toml` file ?
It is possible to run `pip install https://git.disi.dev/name/repo/tarball/main` and
have pip to download the package direcly from Git repo.
For that to work you need to have a `setup.py` file, and `pyproject.toml` is not
supported for that kind of installation.
I think it is easier for example you want to install specific branch or tag you can
do `pip install https://git.disi.dev/name/repo/tarball/{TAG|REVISON|COMMIT}`
People automating CI for your project will be grateful for having a setup.py file
### Why isn't this template made as a cookiecutter template?
I really like [cookiecutter](https://github.com/cookiecutter/cookiecutter) and it is a great way to create new projects,
but for this template I wanted to use the Github `Use this template` button,
to use this template doesn't require to install extra tooling such as cookiecutter.
Just click on [Use this template](https://github.com/rochacbruno/python-project-template/generate) and you are good to go.
The substituions are done using gitea actions and a simple sed script.
### Why `VERSION` is kept in a static plain text file?
I used to have my version inside my main module in a `__version__` variable, then
I had to do some tricks to read that version variable inside the setuptools
`setup.py` file because that would be available only after the installation.
I decided to keep the version in a static file because it is easier to read from
wherever I want without the need to install the package.
e.g: `cat project_name/VERSION` will get the project version without harming
with module imports or anything else, it is useful for CI, logs and debugging.
### Why to include `tests`, `history` and `Containerfile` as part of the release?
The `MANIFEST.in` file is used to include the files in the release, once the
project is released to PyPI all the files listed on MANIFEST.in will be included
even if the files are static or not related to Python.
Some build systems such as RPM, DEB, AUR for some Linux distributions, and also
internal repackaging systems tends to run the tests before the packaging is performed.
The Containerfile can be useful to provide a safer execution environment for
the project when running on a testing environment.
I added those files to make it easier for packaging in different formats.
### Why conftest includes a go_to_tmpdir fixture?
When your project deals with file system operations, it is a good idea to use
a fixture to create a temporary directory and then remove it after the test.
Before executing each test pytest will create a temporary directory and will
change the working directory to that path and run the test.
So the test can create temporary artifacts isolated from other tests.
After the execution Pytest will remove the temporary directory.
### Why this template is not using [pre-commit](https://pre-commit.com/) ?
pre-commit is an excellent tool to automate checks and formatting on your code.
However I figured out that pre-commit adds extra dependency and it an entry barrier
for new contributors.
Having the linting, checks and formatting as simple commands on the [Makefile](Makefile)
makes it easier to undestand and change.
Once the project is bigger and complex, having pre-commit as a dependency can be a good idea.
### Why the CLI is not using click?
I wanted to provide a simple template for a CLI application on the project main entry point
click and typer are great alternatives but are external dependencies and this template
doesn't add dependencies besides those used for development.
### Why this doesn't provide a full example of application using Flask or Django?
as I said before, I want it to be simple and multipurpose, so I decided to not include
external dependencies and programming design decisions.
It is up to you to decide if you want to use Flask or Django and to create your application
the way you think is best.
This template provides utilities in the Makefile to make it easier to you can run:
```bash
$ make init
Which template do you want to apply? [flask, fastapi, click, typer]? > flask
Generating a new project with Flask ...
```
Then the above will download the Flask template and apply it to the project.
## The Makefile
All the utilities for the template and project are on the Makefile
```bash
make
Usage: make <target>
Targets:
help: ## Show the help.
install: ## Install the project in dev mode.
fmt: ## Format code using black & isort.
lint: ## Run pep8, black, mypy linters.
test: lint ## Run tests and generate coverage report.
watch: ## Run tests on every change.
clean: ## Clean unused files.
virtualenv: ## Create a virtual environment.
release: ## Create a new tag for release.
docs: ## Build the documentation.
switch-to-poetry: ## Switch to poetry package manager.
init: ## Initialize the project based on an application template.
```

113
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,113 @@
# How to develop on this project
project_name welcomes contributions from the community.
**You need PYTHON3!**
This instructions are for linux base systems. (Linux, MacOS, BSD, etc.)
## Setting up your own fork of this repo.
- On github interface click on `Fork` button.
- Clone your fork of this repo. `git clone git@github.com:YOUR_GIT_USERNAME/project_urlname.git`
- Enter the directory `cd project_urlname`
- Add upstream repo `git remote add upstream https://github.com/author_name/project_urlname`
## Setting up your own virtual environment
Run `make virtualenv` to create a virtual environment.
then activate it with `source .venv/bin/activate`.
## Install the project in develop mode
Run `make install` to install the project in develop mode.
## Run the tests to ensure everything is working
Run `make test` to run the tests.
## Create a new branch to work on your contribution
Run `git checkout -b my_contribution`
## Make your changes
Edit the files using your preferred editor. (we recommend VIM or VSCode)
## Format the code
Run `make fmt` to format the code.
## Run the linter
Run `make lint` to run the linter.
## Test your changes
Run `make test` to run the tests.
Ensure code coverage report shows `100%` coverage, add tests to your PR.
## Build the docs locally
Run `make docs` to build the docs.
Ensure your new changes are documented.
## Commit your changes
This project uses [conventional git commit messages](https://www.conventionalcommits.org/en/v1.0.0/).
Example: `fix(package): update setup.py arguments 🎉` (emojis are fine too)
## Push your changes to your fork
Run `git push origin my_contribution`
## Submit a pull request
On github interface, click on `Pull Request` button.
Wait CI to run and one of the developers will review your PR.
## Makefile utilities
This project comes with a `Makefile` that contains a number of useful utility.
```bash
make
Usage: make <target>
Targets:
help: ## Show the help.
install: ## Install the project in dev mode.
fmt: ## Format code using black & isort.
lint: ## Run pep8, black, mypy linters.
test: lint ## Run tests and generate coverage report.
watch: ## Run tests on every change.
clean: ## Clean unused files.
virtualenv: ## Create a virtual environment.
release: ## Create a new tag for release.
docs: ## Build the documentation.
switch-to-poetry: ## Switch to poetry package manager.
init: ## Initialize the project based on an application template.
```
## Making a new release
This project uses [semantic versioning](https://semver.org/) and tags releases with `X.Y.Z`
Every time a new tag is created and pushed to the remote repo, github actions will
automatically create a new release on github and trigger a release on PyPI.
For this to work you need to setup a secret called `PIPY_API_TOKEN` on the project settings>secrets,
this token can be generated on [pypi.org](https://pypi.org/account/).
To trigger a new release all you need to do is.
1. If you have changes to add to the repo
* Make your changes following the steps described above.
* Commit your changes following the [conventional git commit messages](https://www.conventionalcommits.org/en/v1.0.0/).
2. Run the tests to ensure everything is working.
4. Run `make release` to create a new tag and push it to the remote repo.
the `make release` will ask you the version number to create the tag, ex: type `0.1.1` when you are asked.
> **CAUTION**: The make release will change local changelog files and commit all the unstaged changes you have.

5
Containerfile Normal file
View File

@ -0,0 +1,5 @@
FROM python:3.7-slim
COPY . /app
WORKDIR /app
RUN pip install .
CMD ["project_name"]

13
HISTORY.md Normal file
View File

@ -0,0 +1,13 @@
Changelog
=========
0.1.2 (2021-08-14)
------------------
- Fix release, README and windows CI. [Bruno Rocha]
- Release: version 0.1.0. [Bruno Rocha]
0.1.0 (2021-08-14)
------------------
- Add release command. [Bruno Rocha]

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>

5
MANIFEST.in Normal file
View File

@ -0,0 +1,5 @@
include LICENSE
include HISTORY.md
include Containerfile
graft tests
graft project_name

122
Makefile Normal file
View File

@ -0,0 +1,122 @@
.ONESHELL:
ENV_PREFIX=$(shell python -c "if __import__('pathlib').Path('.venv/bin/pip').exists(): print('.venv/bin/')")
USING_POETRY=$(shell grep "tool.poetry" pyproject.toml && echo "yes")
.PHONY: help
help: ## Show the help.
@echo "Usage: make <target>"
@echo ""
@echo "Targets:"
@fgrep "##" Makefile | fgrep -v fgrep
.PHONY: show
show: ## Show the current environment.
@echo "Current environment:"
@if [ "$(USING_POETRY)" ]; then poetry env info && exit; fi
@echo "Running using $(ENV_PREFIX)"
@$(ENV_PREFIX)python -V
@$(ENV_PREFIX)python -m site
.PHONY: install
install: ## Install the project in dev mode.
@if [ "$(USING_POETRY)" ]; then poetry install && exit; fi
@echo "Don't forget to run 'make virtualenv' if you got errors."
$(ENV_PREFIX)pip install -e .[test]
.PHONY: fmt
fmt: ## Format code using black & isort.
$(ENV_PREFIX)isort project_name/
$(ENV_PREFIX)black -l 79 project_name/
$(ENV_PREFIX)black -l 79 tests/
.PHONY: lint
lint: ## Run pep8, black, mypy linters.
$(ENV_PREFIX)flake8 project_name/
$(ENV_PREFIX)black -l 79 --check project_name/
$(ENV_PREFIX)black -l 79 --check tests/
$(ENV_PREFIX)mypy --ignore-missing-imports project_name/
.PHONY: test
test: lint ## Run tests and generate coverage report.
$(ENV_PREFIX)pytest -v --cov-config .coveragerc --cov=project_name -l --tb=short --maxfail=1 tests/
$(ENV_PREFIX)coverage xml
$(ENV_PREFIX)coverage html
.PHONY: watch
watch: ## Run tests on every change.
ls **/**.py | entr $(ENV_PREFIX)pytest -s -vvv -l --tb=long --maxfail=1 tests/
.PHONY: clean
clean: ## Clean unused files.
@find ./ -name '*.pyc' -exec rm -f {} \;
@find ./ -name '__pycache__' -exec rm -rf {} \;
@find ./ -name 'Thumbs.db' -exec rm -f {} \;
@find ./ -name '*~' -exec rm -f {} \;
@rm -rf .cache
@rm -rf .pytest_cache
@rm -rf .mypy_cache
@rm -rf build
@rm -rf dist
@rm -rf *.egg-info
@rm -rf htmlcov
@rm -rf .tox/
@rm -rf docs/_build
.PHONY: virtualenv
virtualenv: ## Create a virtual environment.
@if [ "$(USING_POETRY)" ]; then poetry install && exit; fi
@echo "creating virtualenv ..."
@rm -rf .venv
@python3 -m venv .venv
@./.venv/bin/pip install -U pip
@./.venv/bin/pip install -e .[test]
@echo
@echo "!!! Please run 'source .venv/bin/activate' to enable the environment !!!"
.PHONY: release
release: ## Create a new tag for release.
@echo "WARNING: This operation will create s version tag and push to github"
@read -p "Version? (provide the next x.y.z semver) : " TAG
@echo "$${TAG}" > project_name/VERSION
@$(ENV_PREFIX)gitchangelog > HISTORY.md
@git add project_name/VERSION HISTORY.md
@git commit -m "release: version $${TAG} 🚀"
@echo "creating git tag : $${TAG}"
@git tag $${TAG}
@git push -u origin HEAD --tags
@echo "Github Actions will detect the new tag and release the new version."
.PHONY: docs
docs: ## Build the documentation.
@echo "building documentation ..."
@$(ENV_PREFIX)mkdocs build
URL="site/index.html"; xdg-open $$URL || sensible-browser $$URL || x-www-browser $$URL || gnome-open $$URL || open $$URL
.PHONY: switch-to-poetry
switch-to-poetry: ## Switch to poetry package manager.
@echo "Switching to poetry ..."
@if ! poetry --version > /dev/null; then echo 'poetry is required, install from https://python-poetry.org/'; exit 1; fi
@rm -rf .venv
@poetry init --no-interaction --name=a_flask_test --author=rochacbruno
@echo "" >> pyproject.toml
@echo "[tool.poetry.scripts]" >> pyproject.toml
@echo "project_name = 'project_name.__main__:main'" >> pyproject.toml
@cat requirements.txt | while read in; do poetry add --no-interaction "$${in}"; done
@cat requirements-test.txt | while read in; do poetry add --no-interaction "$${in}" --dev; done
@poetry install --no-interaction
@mkdir -p .gitea/backup
@mv requirements* .gitea/backup
@mv setup.py .gitea/backup
@echo "You have switched to https://python-poetry.org/ package manager."
@echo "Please run 'poetry shell' or 'poetry run project_name'"
.PHONY: init
init: ## Initialize the project based on an application template.
@./.gitea/init.sh
# This project has been generated from rochacbruno/python-project-template
# __author__ = 'rochacbruno'
# __repo__ = https://github.com/rochacbruno/python-project-template
# __sponsor__ = https://github.com/sponsors/rochacbruno/

View File

@ -1,3 +1,81 @@
# Python
Python project template
# Python Project Template
A low dependency and really simple to start project template for Python Projects.
See also
- [Flask-Project-Template](https://github.com/rochacbruno/flask-project-template/) for a full feature Flask project including database, API, admin interface, etc.
- [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.
### HOW TO USE THIS TEMPLATE
1. Choose a name for your project
(e.g. `my_awesome_project` recommendation is to use all lowercase and underscores separation for repo names.)
2. Replace all text instances of "project_name" in this repo with your new name
3. If you want [codecov](https://about.codecov.io/sign-up/) Reports and Automatic Release to [PyPI](https://pypi.org)
On the new repository `settings->secrets` add your `PYPI_API_TOKEN` and `CODECOV_TOKEN` (get the tokens on respective websites)
4. Read the file [CONTRIBUTING.md](CONTRIBUTING.md)
5. Then clone your new project and happy coding!
> **NOTE**: **WAIT** until first CI run on github actions before cloning your new project.
### What is included on this template?
- 🖼️ Templates for starting multiple application types:
* **Basic low dependency** Python program (default) [use this template](https://github.com/rochacbruno/python-project-template/generate)
* **Flask** with database, admin interface, restapi and authentication [use this template](https://github.com/rochacbruno/flask-project-template/generate).
**or Run `make init` after cloning to generate a new project based on a template.**
- 📦 A basic [setup.py](setup.py) file to provide installation, packaging and distribution for your project.
Template uses setuptools because it's the de-facto standard for Python packages, you can run `make switch-to-poetry` later if you want.
- 🤖 A [Makefile](Makefile) with the most useful commands to install, test, lint, format and release your project.
- 📃 Documentation structure using [mkdocs](http://www.mkdocs.org)
- 💬 Auto generation of change log using **gitchangelog** to keep a HISTORY.md file automatically based on your commit history on every release.
- 🐋 A simple [Containerfile](Containerfile) to build a container image for your project.
`Containerfile` is a more open standard for building container images than Dockerfile, you can use buildah or docker with this file.
- 🧪 Testing structure using [pytest](https://docs.pytest.org/en/latest/)
- ✅ Code linting using [flake8](https://flake8.pycqa.org/en/latest/)
- 📊 Code coverage reports using [codecov](https://about.codecov.io/sign-up/)
- 🛳️ Automatic release to [PyPI](https://pypi.org) using [twine](https://twine.readthedocs.io/en/latest/) and github actions.
- 🎯 Entry points to execute your program using `python -m <project_name>` or `$ project_name` with basic CLI argument parsing.
- 🔄 Continuous integration using [Github Actions](.gitea/workflows/) with jobs to lint, test and release your project on Linux, Mac and Windows environments.
> Curious about architectural decisions on this template? read [ABOUT_THIS_TEMPLATE.md](ABOUT_THIS_TEMPLATE.md)
> 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.
[❤️ Sponsor this project](https://github.com/sponsors/rochacbruno/)
<!-- DELETE THE LINES ABOVE THIS AND WRITE YOUR PROJECT README BELOW -->
---
# project_name
[![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)
[![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)
project_description
## Install it from PyPI
```bash
pip install project_name
```
## Usage
```py
from project_name import BaseClass
from project_name import base_function
BaseClass().base_method()
base_function()
```
```bash
$ python -m project_name
#or
$ project_name
```
## Development
Read the [CONTRIBUTING.md](CONTRIBUTING.md) file.

17
docs/index.md Normal file
View File

@ -0,0 +1,17 @@
# Welcome to MkDocs
For full documentation visit [mkdocs.org](https://www.mkdocs.org).
## Commands
* `mkdocs new [dir-name]` - Create a new project.
* `mkdocs serve` - Start the live-reloading docs server.
* `mkdocs build` - Build the documentation site.
* `mkdocs -h` - Print help message and exit.
## Project layout
mkdocs.yml # The configuration file.
docs/
index.md # The documentation homepage.
... # Other markdown pages, images and other files.

View File

@ -0,0 +1,15 @@
### Summary :memo:
_Write an overview about it._
### Details
_Describe more what you did on changes._
1. (...)
2. (...)
### Bugfixes :bug: (delete if dind't have any)
-
### Checks
- [ ] Closed #798
- [ ] Tested Changes
- [ ] Stakeholder Approval

68
gitea/init.sh Executable file
View File

@ -0,0 +1,68 @@
#!/usr/bin/env bash
overwrite_template_dir=0
while getopts t:o flag
do
case "${flag}" in
t) template=${OPTARG};;
o) overwrite_template_dir=1;;
esac
done
if [ -z "${template}" ]; then
echo "Available templates: flask"
read -p "Enter template name: " template
fi
repo_urlname=$(basename -s .git `git config --get remote.origin.url`)
repo_name=$(basename -s .git `git config --get remote.origin.url` | tr '-' '_' | tr '[:upper:]' '[:lower:]')
repo_owner=$(git config --get remote.origin.url | awk -F ':' '{print $2}' | awk -F '/' '{print $1}')
echo "Repo name: ${repo_name}"
echo "Repo owner: ${repo_owner}"
echo "Repo urlname: ${repo_urlname}"
if [ -f ".gitea/workflows/rename_project.yml" ]; then
.gitea/rename_project.sh -a "${repo_owner}" -n "${repo_name}" -u "${repo_urlname}" -d "Awesome ${repo_name} created by ${repo_owner}"
fi
function download_template {
rm -rf "${template_dir}"
mkdir -p .gitea/templates
git clone "${template_url}" "${template_dir}"
}
echo "Using template:${template}"
template_url="https://github.com/rochacbruno/${template}-project-template"
template_dir=".gitea/templates/${template}"
if [ -d "${template_dir}" ]; then
# Template directory already exists
if [ "${overwrite_template_dir}" -eq 1 ]; then
# user passed -o flag, delete and re-download
echo "Overwriting ${template_dir}"
download_template
else
# Ask user if they want to overwrite
echo "Directory ${template_dir} already exists."
read -p "Do you want to overwrite it? [y/N] " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "Overwriting ${template_dir}"
download_template
else
# User decided not to overwrite
echo "Using existing ${template_dir}"
fi
fi
else
# Template directory does not exist, download it
echo "Downloading ${template_url}"
download_template
fi
echo "Applying ${template} template to this project"}
./.gitea/templates/${template}/apply.sh -a "${repo_owner}" -n "${repo_name}" -u "${repo_urlname}" -d "Awesome ${repo_name} created by ${repo_owner}"
# echo "Removing temporary template files"
# rm -rf .gitea/templates/${template}
echo "Done! review, commit and push the changes"

3
gitea/release_message.sh Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env bash
previous_tag=$(git tag --sort=-creatordate | sed -n 2p)
git shortlog "${previous_tag}.." | sed 's/^./ &/'

36
gitea/rename_project.sh Executable file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env bash
while getopts a:n:u:d: flag
do
case "${flag}" in
a) author=${OPTARG};;
n) name=${OPTARG};;
u) urlname=${OPTARG};;
d) description=${OPTARG};;
esac
done
echo "Author: $author";
echo "Project Name: $name";
echo "Project URL name: $urlname";
echo "Description: $description";
echo "Renaming project..."
original_author="author_name"
original_name="project_name"
original_urlname="project_urlname"
original_description="project_description"
# for filename in $(find . -name "*.*")
for filename in $(git ls-files)
do
sed -i "s/$original_author/$author/g" $filename
sed -i "s/$original_name/$name/g" $filename
sed -i "s/$original_urlname/$urlname/g" $filename
sed -i "s/$original_description/$description/g" $filename
echo "Renamed $filename"
done
mv project_name $name
# This command runs only once on GHA!
rm -rf .gitea/template.yml

1
gitea/template.yml Normal file
View File

@ -0,0 +1 @@
author: rochacbruno

92
gitea/workflows/main.yml Normal file
View File

@ -0,0 +1,92 @@
# This is a basic workflow to help you get started with Actions
name: CI
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [ main ]
pull_request:
branches: [ main ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
linter:
strategy:
fail-fast: false
matrix:
python-version: [3.9]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install project
run: make install
- name: Run linter
run: make lint
tests_linux:
needs: linter
strategy:
fail-fast: false
matrix:
python-version: [3.9]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install project
run: make install
- name: Run tests
run: make test
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v3
# with:
# fail_ci_if_error: true
tests_mac:
needs: linter
strategy:
fail-fast: false
matrix:
python-version: [3.9]
os: [macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install project
run: make install
- name: Run tests
run: make test
tests_win:
needs: linter
strategy:
fail-fast: false
matrix:
python-version: [3.9]
os: [windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install Pip
run: pip install --user --upgrade pip
- name: Install project
run: pip install -e .[test]
- name: run tests
run: pytest -s -vvvv -l --tb=long tests

View File

@ -0,0 +1,52 @@
name: Upload Python Package
permissions:
contents: write
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- '*' # Push events to matching v*, i.e. v1.0, v20.15.10
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
release:
name: Create Release
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v3
with:
# by default, it uses a depth of 1
# this fetches all history so that we can read each commit
fetch-depth: 0
- name: Generate Changelog
run: .gitea/release_message.sh > release_message.md
- name: Release
uses: softprops/action-gh-release@v1
with:
body_path: release_message.md
deploy:
needs: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*

View File

@ -0,0 +1,42 @@
name: Rename the project from template
on: [push]
permissions: write-all
jobs:
rename-project:
if: ${{ !contains (github.repository, '/python-project-template') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
# by default, it uses a depth of 1
# this fetches all history so that we can read each commit
fetch-depth: 0
ref: ${{ github.head_ref }}
- run: echo "REPOSITORY_NAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}' | tr '-' '_' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
shell: bash
- run: echo "REPOSITORY_URLNAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_ENV
shell: bash
- run: echo "REPOSITORY_OWNER=$(echo '${{ github.repository }}' | awk -F '/' '{print $1}')" >> $GITHUB_ENV
shell: bash
- name: Is this still a template
id: is_template
run: echo "::set-output name=is_template::$(ls .gitea/template.yml &> /dev/null && echo true || echo false)"
- name: Rename the project
if: steps.is_template.outputs.is_template == 'true'
run: |
echo "Renaming the project with -a(author) ${{ env.REPOSITORY_OWNER }} -n(name) ${{ env.REPOSITORY_NAME }} -u(urlname) ${{ env.REPOSITORY_URLNAME }}"
.gitea/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 }}"
- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: "✅ Ready to clone and code."
# commit_options: '--amend --no-edit'
push_options: --force

2
mkdocs.yml Normal file
View File

@ -0,0 +1,2 @@
site_name: project_name
theme: readthedocs

1
project_name/VERSION Normal file
View File

@ -0,0 +1 @@
0.1.0

0
project_name/__init__.py Normal file
View File

6
project_name/__main__.py Normal file
View File

@ -0,0 +1,6 @@
"""Entry point for project_name."""
from project_name.cli import main # pragma: no cover
if __name__ == "__main__": # pragma: no cover
main()

17
project_name/base.py Normal file
View File

@ -0,0 +1,17 @@
"""
project_name base module.
This is the principal module of the project_name project.
here you put your main classes and objects.
Be creative! do whatever you want!
If you want to replace this with a Flask application run:
$ make init
and then choose `flask` as template.
"""
# example constant variable
NAME = "project_name"

28
project_name/cli.py Normal file
View File

@ -0,0 +1,28 @@
"""CLI interface for project_name project.
Be creative! do whatever you want!
- Install click or typer and create a CLI app
- Use builtin argparse
- Start a web application
- Import things from your .base module
"""
def main(): # pragma: no cover
"""
The main function executes on commands:
`python -m project_name` and `$ project_name `.
This is your program's entry point.
You can change this function to do whatever you want.
Examples:
* Run a test suite
* Run a server
* Do some other stuff
* Run a command line application (Click, Typer, ArgParse)
* List all available tasks
* Run an application (Flask, FastAPI, Django, etc.)
"""
print("This will do something")

10
requirements-test.txt Normal file
View File

@ -0,0 +1,10 @@
# These requirements are for development and testing only, not for production.
pytest
coverage
flake8
black
isort
pytest-cov
mypy
gitchangelog
mkdocs

5
requirements.txt Normal file
View File

@ -0,0 +1,5 @@
# This template is a low-dependency template.
# By default there is no requirements added here.
# Add the requirements you need to this file.
# or run `make init` to create this file automatically based on the template.
# You can also run `make switch-to-poetry` to use the poetry package manager.

46
setup.py Normal file
View File

@ -0,0 +1,46 @@
"""Python setup.py for project_name package"""
import io
import os
from setuptools import find_packages, setup
def read(*paths, **kwargs):
"""Read the contents of a text file safely.
>>> read("project_name", "VERSION")
'0.1.0'
>>> read("README.md")
...
"""
content = ""
with io.open(
os.path.join(os.path.dirname(__file__), *paths),
encoding=kwargs.get("encoding", "utf8"),
) as open_file:
content = open_file.read().strip()
return content
def read_requirements(path):
return [
line.strip()
for line in read(path).split("\n")
if not line.startswith(('"', "#", "-", "git+"))
]
setup(
name="project_name",
version=read("project_name", "VERSION"),
description="project_description",
url="https://git.disi.dev/author_name/project_urlname/",
long_description=read("README.md"),
long_description_content_type="text/markdown",
author="author_name",
packages=find_packages(exclude=["tests", ".gitea"]),
install_requires=read_requirements("requirements.txt"),
entry_points={
"console_scripts": ["project_name = project_name.__main__:main"]
},
extras_require={"test": read_requirements("requirements-test.txt")},
)

0
tests/__init__.py Normal file
View File

14
tests/conftest.py Normal file
View File

@ -0,0 +1,14 @@
import sys
import pytest
# each test runs on cwd to its temp dir
@pytest.fixture(autouse=True)
def go_to_tmpdir(request):
# Get the fixture dynamically by its name.
tmpdir = request.getfixturevalue("tmpdir")
# ensure local test created packages can be imported
sys.path.insert(0, str(tmpdir))
# Chdir only for the duration of the test.
with tmpdir.as_cwd():
yield

5
tests/test_base.py Normal file
View File

@ -0,0 +1,5 @@
from project_name.base import NAME
def test_base():
assert NAME == "project_name"