Repository: FrancescoSaverioZuppichini/python-template
Branch: main
Commit: 06459b247e3a
Files: 14
Total size: 14.5 KB
Directory structure:
gitextract_ov64cyve/
├── .github/
│ └── workflows/
│ ├── build.yml
│ ├── publish.yml
│ └── test.yml
├── .gitignore
├── .python-version
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── examples/
│ └── main.py
├── pyproject.toml
├── src/
│ └── jokes/
│ ├── __init__.py
│ └── jokes.py
└── tests/
└── test_jokes.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/build.yml
================================================
#
name: Create and publish a Docker image
# Configures this workflow to run every time a change is pushed to the branch called `release` or a new release is created.
on:
release:
types: [published]
push:
branches: ["release"]
# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds.
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
jobs:
build-and-push-image:
runs-on: ubuntu-latest
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
permissions:
contents: read
packages: write
attestations: write
id-token: write
#
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
- name: Build and push Docker image
id: push
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# This step generates an artifact attestation for the image, which is an unforgeable statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see "[AUTOTITLE](/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds)."
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v2
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true
================================================
FILE: .github/workflows/publish.yml
================================================
name: Publish
on:
push:
branches:
- main
jobs:
build-format-and-publish-to-pypi:
runs-on: ubuntu-latest
# FROM https://github.com/pypa/gh-action-pypi-publish
environment:
name: pypi
url: https://pypi.org/p/python-template-zuppif
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
- name: 📦 Install uv
uses: astral-sh/setup-uv@v4
- name: 🐍 Set up Python
uses: actions/setup-python@v5
with:
python-version-file: ".python-version"
- name: 🦾 Install the project
run: uv sync
- name: 💅 Format
run: uv run ruff format
- name: 🦾 Build
run: uv build
- name: 🚀 Publish
uses: pypa/gh-action-pypi-publish@release/v1
================================================
FILE: .github/workflows/test.yml
================================================
name: Test
on:
pull_request:
branches: [main, dev]
jobs:
build-lint-format-and-test:
runs-on: ubuntu-latest
steps:
- name: 🛎️ Checkout
uses: actions/checkout@v4
- name: 📦 Install uv
uses: astral-sh/setup-uv@v4
- name: 🐍 Set up Python
uses: actions/setup-python@v5
with:
python-version-file: ".python-version"
- name: 🦾 Install the project
run: |
uv sync --all-extras --dev
uv pip install -e .
- name: 💅 Link & Format Check
run: make check_code_quality
- name: 🧪 Test
run: uv run pytest tests/
================================================
FILE: .gitignore
================================================
# ruff
.ruff_cache
# 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/
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/
cover/
# 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
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
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
# 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
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.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/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__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/
# 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/
# PyPI configuration file
.pypirc
================================================
FILE: .python-version
================================================
3.12
================================================
FILE: Dockerfile
================================================
# Build stage
FROM python:3.12-slim AS builder
# Update image
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
# Set working directory
WORKDIR /app
# Install the project without the the source code (only dependencies)
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --no-install-project --no-dev --compile-bytecode
# Install the project's source packages
ADD .python-version pyproject.toml uv.lock src/ /app/
RUN --mount=type=cache,target=/root/.cache/uv \
uv pip install --compile-bytecode .
# Use alpine for the final image to reduce the total size
FROM python:3.12-alpine
# Copy the installed environment from builder
COPY --from=builder --chown=app:app /app /app
# Set working directory
WORKDIR /app
# Configure PATH for executables, packages are in the /app/.venv folder
ENV PATH="/app/.venv/bin:$PATH"
# Run command
CMD ["make_me_laugh"]
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2024 Francesco Saverio Zuppichini
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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 OR COPYRIGHT HOLDERS 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.
================================================
FILE: Makefile
================================================
.PHONY: style check_code_quality
export PYTHONPATH = .
check_dirs := src
style:
uv run ruff format $(check_dirs)
uv run ruff check --select I --fix
link:
uv run ruff check --fix
check_code_quality:
uv run ruff check $(check_dirs)
# check sort
uv run ruff check -e
uv run ruff check --select I -e
publish:
uv build
uv publish --token $UV_PUBLISH_TOKEN
================================================
FILE: README.md
================================================
# Python Template 🐍
[](https://pypi.org/project/python-template-zuppif/)
A template for a python project for 2025
Features:
- [x] 🛠️ configuration in a single file [`pyproject.toml`](pyproject.toml)
- [x] 📦 [`uv`](https://docs.astral.sh/uv/) as package manager
- [x] 💅 [`ruff`](https://docs.astral.sh/ruff/) for linting and formatting
- [x] 🧪 [`pytest`](https://docs.pytest.org/en/stable/)
- [x] 🧹 [`Makefile`](Makefile) with code quality checks
- [ ] 📚 auto doc generation
- [x] 🐳 CI/CD Optimized Docker Image runs when a new *release* is created pushing to gh registry
- [x] 🦾 GitHub actions:
- [x] auto publish to [`pypi`](https://pypi.org/) on push on `main`
- [ ] auto creating a new tag on push on `main`, sync versions
- [x] run `tests` and `lint` on `dev` and `main` when a PR is open
## Getting started
### Installation
To set it up and run
```bash
uv sync
```
Then
```bash
python main.py
```
Will output a random joke
```
Why did the cow in the pasture get promoted at work? ... Because he is OUT-STANDING in his field!
```
You can now run, for example, a function defined as `scripts` in the [`pyproject.toml`](pyproject.toml)
```bash
make_me_laugh
```
### Linting
```
ruff check
```
### Formatting
```
ruff format
```
## CI/CD
### Tests
Tests inside `/tests` are run using [`pytest`](https://docs.pytest.org/en/stable/) on PR both on `dev` and `main`
### Publish Package
In order to publish to [pypi](https://pypi.org/) you need to create a publisher on pypi.
This is explained [here](https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/) and [here](https://docs.pypi.org/trusted-publishers/)
In practice go your pypi project -> Manage Project -> Publishing, scroll and "add a new publisher"
### Docker
[`Dockerfile`](Dockerfile) contains a multi stage build that uses `--compile-bytecode` to compite your package. For this example, the resulting image is just
```bash
docker build -t python-template .
```
```
REPOSITORY TAG IMAGE ID CREATED SIZE
python-template latest 1ded7d260b1c 58 seconds ago 55.4MB
```
The image is build using the [`build`](.github/workflows/build.yml) workflow when a new *release* is created
================================================
FILE: examples/main.py
================================================
from jokes import make_me_laugh
make_me_laugh()
================================================
FILE: pyproject.toml
================================================
[project]
# https://docs.astral.sh/uv/concepts/projects/config/
name = "python-template-zuppif"
version = "0.1.3"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["requests>=2.32.3"]
[dependency-groups]
dev = ["pytest>=8.3.4", "ruff>=0.8.4"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/jokes"]
[tool.ruff]
# https://docs.astral.sh/ruff/configuration/
target-version = "py312"
[project.scripts]
# will run the function by just typing `make_me_laugh` in the cli
make_me_laugh = "jokes:make_me_laugh"
================================================
FILE: src/jokes/__init__.py
================================================
from .jokes import get_joke
def make_me_laugh():
joke = get_joke()
print(f"{joke["preambula"]} ... {joke['punchline']}")
================================================
FILE: src/jokes/jokes.py
================================================
from random import choice
from typing import TypedDict
import requests
JOKES_FILE_URL = "https://gist.githubusercontent.com/FrancescoSaverioZuppichini/8e7614da3b156ccaa29d50211b790146/raw/79b00cdaa023903629052426b24cdee7212229c3/jokes.txt"
class Joke(TypedDict):
preambula: str
punchline: str
def get_joke() -> Joke:
res = requests.get(JOKES_FILE_URL)
content = res.text
jokes = content.strip().split("\n")
joke = choice(jokes)
joke_splitted = joke.split("<>")
return Joke(preambula=joke_splitted[0], punchline=joke_splitted[1])
================================================
FILE: tests/test_jokes.py
================================================
from jokes import get_joke
def test_jokes():
joke = get_joke()
assert "preambula" in joke
assert "punchline" in joke
gitextract_ov64cyve/
├── .github/
│ └── workflows/
│ ├── build.yml
│ ├── publish.yml
│ └── test.yml
├── .gitignore
├── .python-version
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── examples/
│ └── main.py
├── pyproject.toml
├── src/
│ └── jokes/
│ ├── __init__.py
│ └── jokes.py
└── tests/
└── test_jokes.py
SYMBOL INDEX (4 symbols across 3 files) FILE: src/jokes/__init__.py function make_me_laugh (line 4) | def make_me_laugh(): FILE: src/jokes/jokes.py class Joke (line 9) | class Joke(TypedDict): function get_joke (line 14) | def get_joke() -> Joke: FILE: tests/test_jokes.py function test_jokes (line 4) | def test_jokes():
Condensed preview — 14 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (16K chars).
[
{
"path": ".github/workflows/build.yml",
"chars": 3379,
"preview": "#\nname: Create and publish a Docker image\n\n# Configures this workflow to run every time a change is pushed to the branch"
},
{
"path": ".github/workflows/publish.yml",
"chars": 875,
"preview": "name: Publish\non:\n push:\n branches:\n - main\njobs:\n build-format-and-publish-to-pypi:\n runs-on: ubuntu-lates"
},
{
"path": ".github/workflows/test.yml",
"chars": 638,
"preview": "name: Test\non:\n pull_request:\n branches: [main, dev]\n\njobs:\n build-lint-format-and-test:\n runs-on: ubuntu-latest"
},
{
"path": ".gitignore",
"chars": 3434,
"preview": "# ruff\n.ruff_cache\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Dis"
},
{
"path": ".python-version",
"chars": 5,
"preview": "3.12\n"
},
{
"path": "Dockerfile",
"chars": 1145,
"preview": "# Build stage\nFROM python:3.12-slim AS builder\n\n# Update image\nRUN apt-get update && apt-get install -y --no-install-rec"
},
{
"path": "LICENSE",
"chars": 1085,
"preview": "MIT License\n\nCopyright (c) 2024 Francesco Saverio Zuppichini\n\nPermission is hereby granted, free of charge, to any perso"
},
{
"path": "Makefile",
"chars": 367,
"preview": ".PHONY: style check_code_quality\n\nexport PYTHONPATH = .\ncheck_dirs := src\n\nstyle:\n\tuv run ruff format $(check_dirs)\n\tuv"
},
{
"path": "README.md",
"chars": 2352,
"preview": "# Python Template 🐍\n[](https://pypi.org/project/pyt"
},
{
"path": "examples/main.py",
"chars": 49,
"preview": "from jokes import make_me_laugh\n\nmake_me_laugh()\n"
},
{
"path": "pyproject.toml",
"chars": 641,
"preview": "[project]\n# https://docs.astral.sh/uv/concepts/projects/config/\nname = \"python-template-zuppif\"\nversion = \"0.1.3\"\ndescri"
},
{
"path": "src/jokes/__init__.py",
"chars": 131,
"preview": "from .jokes import get_joke\n\n\ndef make_me_laugh():\n joke = get_joke()\n print(f\"{joke[\"preambula\"]} ... {joke['punc"
},
{
"path": "src/jokes/jokes.py",
"chars": 567,
"preview": "from random import choice\nfrom typing import TypedDict\n\nimport requests\n\nJOKES_FILE_URL = \"https://gist.githubuserconten"
},
{
"path": "tests/test_jokes.py",
"chars": 131,
"preview": "from jokes import get_joke\n\n\ndef test_jokes():\n joke = get_joke()\n assert \"preambula\" in joke\n assert \"punchlin"
}
]
About this extraction
This page contains the full source code of the FrancescoSaverioZuppichini/python-template GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 14 files (14.5 KB), approximately 4.3k tokens, and a symbol index with 4 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.