Initial commit

This commit is contained in:
Projects 2024-11-11 06:11:21 -08:00
commit b461aefe5f
31 changed files with 1150 additions and 0 deletions

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://git.disi.dev/Templates/${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

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

@ -0,0 +1,88 @@
# 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
# 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: ${{ !endsWith (github.repository, 'Templates/Python') }}
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: ${{ gitea.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

View File

@ -0,0 +1,26 @@
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
name: SonarQube Scan
jobs:
sonarqube:
name: SonarQube Trigger
runs-on: ubuntu-latest
steps:
- name: Checking out
uses: actions/checkout@v4
with:
# Disabling shallow clone is recommended for improving relevancy of reporting
fetch-depth: 0
- name: SonarQube Scan
uses: sonarsource/sonarqube-scan-action@master
env:
SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONARQUBE_HOST }}
with:
args: >
-Dsonar.projectKey=project_name

132
.gitignore vendored Normal file
View File

@ -0,0 +1,132 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# templates
.gitea/templates/*

195
ABOUT_THIS_TEMPLATE.md Normal file
View File

@ -0,0 +1,195 @@
# 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,
to use this template doesn't require to install extra tooling such as cookiecutter.
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 artifactory 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.
```

110
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,110 @@
# 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 gitea interface click on `Fork` button.
- Clone your fork of this repo. `git clone git@git.disi.dev:YOUR_GIT_USERNAME/project_urlname.git`
- Enter the directory `cd project_urlname`
- Add upstream repo `git remote add upstream https://git.disi.dev/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 gitea 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, gitea actions will
automatically create a new release on gitea.
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 a version tag and push to gitea"
@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 "Gitea 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/

62
README.md Normal file
View File

@ -0,0 +1,62 @@
# Python Project Template
A low dependency and really simple to start project template for Python Projects.
See also
- [Flask-Project-Template](https://git.disi.dev/Templates/Flask/) for a full feature Flask project including database, API, admin interface, etc.
### HOW TO USE THIS TEMPLATE
1. Create a new repository from this template and choose a name for your project
(e.g. `my_awesome_project` - recommendation is to use all lowercase and underscores separation for repo names.)
2. Wait until the first run of CI finishes (Gitea Actions will process the template and commit to your new repo)
3. Read the file [CONTRIBUTING.md](CONTRIBUTING.md)
4. Then clone your new project and happy coding!
> **NOTE**: **WAIT** until first CI run on gitea actions before cloning your new project.
### What is included on this template?
- 🖼️ Templates for starting multiple application types:
**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/)
- 🎯 Entry points to execute your program using `python -m <project_name>` or `$ project_name` with basic CLI argument parsing.
- 🔄 Continuous integration using [Gitea 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)
<!-- DELETE THE LINES ABOVE THIS AND WRITE YOUR PROJECT README BELOW -->
---
# project_name
project_description
## 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.

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"