badgepy
badgepy is a Python library and command-line tool for generating GitHub-style badges as SVG images — no external service required. Badges are rendered locally, offline, and fully under your control. It also provides a local shields.io-style badge generator — generate badges from CI reports (JUnit, Cobertura), use preset recipes for build/coverage/version/license badges, and serve them via a Flask server, all without relying on external services.
The visual design follows the Shields specification and is compatible with Shields.io.
Shields.io vs badgepy
Both tools produce identical-looking badges, but they work very differently. Here's how to choose:
| Shields.io | badgepy | |
|---|---|---|
| How it works | HTTP service — you request a badge URL and get back an SVG | Python library/CLI — generates SVGs locally |
| Internet required | Yes (or self-host) | No — works fully offline |
| Setup | Zero — just use a URL in your README | pip install badgepy |
| Customization | URL query parameters only | Full programmatic control in Python |
| CI integration | Via shields.io endpoint JSON | Native parsers for JUnit, Cobertura, generic JSON |
| Rate limits | Yes on shields.io (no limits if self-hosted) | None |
| Preset badges | Static badge only | build, coverage, version, license, custom, progress |
| Output | Rendered in browser from URL | SVG string, file, or served from your own app |
Choose Shields.io if:
- You just need a few badges in a README and don't want to install anything
- Your badge data is already exposed via a public API
- You're comfortable with the hosted service's availability and rate limits
Choose badgepy if:
- You're generating badges in a CI/CD pipeline and want offline reliability
- You need programmatic control over badge generation from Python
- You're parsing local test/coverage reports (JUnit, Cobertura) into badges
- You want to serve badges from your own application
- You need zero network dependencies at badge generation time
💡 Already using Shields.io static badges? See the Shields.io Migration Guide for a drop-in replacement path.
Sponsors
badgepy is supported by Canonical through thanks.dev. Thank you, Canonical, for supporting open source maintainers and helping make continued maintenance of badgepy possible.
If your team depends on badgepy or wants to support independent open source maintenance, please consider sponsoring the project through thanks.dev or GitHub Sponsors.
Getting Started
Installing
pip install badgepy
Verify the installation:
python -m badgepy --left-text=build --right-text=failure --right-color='#c00' --browser
You should see a badge like this in your browser:
Usage
badgepy can be used from the command line and as a Python library.
Prefer to start with the CLI? It's a great way to experiment before writing code. Prefer to see a running server? Check out the example Flask server.
Command Line
Full documentation of all command-line arguments:
badgepy --help
A complete example demonstrating every option:
badgepy \
--left-text=complete \
--right-text=example \
--left-color=green \
--right-color='#fb3' \
--left-link=http://www.complete.com/ \
--right-link=http://www.example.com \
--logo='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAD0lEQVQI12P4zwAD/xkYAA/+Af8iHnLUAAAAAElFTkSuQmCC' \
--embed-logo \
--whole-title="Badge Title" \
--left-title="Left Title" \
--right-title="Right Title" \
--browser
Logos
The --logo option accepts a URL:
badgepy \
--left-text="python" \
--right-text="3.10, 3.11, 3.12, 3.13, 3.14" \
--whole-link="https://www.python.org/" \
--browser \
--logo='https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/python.svg'
Use --embed-logo to inline the logo data directly into the SVG, saving an HTTP request at render time. This is especially useful offline or in browsers that block external image references.
Use --logo-width for wide custom logos and --font-family when you need a
specific SVG font stack:
badgepy \
--left-text=downloads \
--right-text=2.7G \
--logo='data:image/svg+xml;base64,...' \
--logo-width=28 \
--font-family="Open Sans,sans-serif" \
-o badges/downloads.svg
Titles
The title element is shown as a tooltip by browsers but is currently filtered by GitHub.
--whole-title applies to the whole badge, while --left-title, --right-title and --center-title are shown when hovering that part.
Styles
--style selects the look of the badge, matching the shields.io styles of the same name:
| Style | Corners | Gradient |
|---|---|---|
flat (default) |
rounded | yes |
flat-square |
square | no |
badgepy --left-text=build --right-text=passing --right-color=brightgreen --style=flat-square
Everything else — geometry, text, colors, logos, links and titles — is identical between the two.
--style applies to the badge command; preset and CI-report badges are always flat.
Library
from badgepy import badge
s = badge(left_text='coverage', right_text='23%', right_color='red')
# s is a string containing the badge as an SVG image.
print(s[:40]) # => <svg height="20" width="191.0" xmlns="ht
Keyword arguments mirror the CLI flags, with underscores instead of hyphens (e.g. --left-text → left_text=).
Serving Badges from a Web App
badgepy can be used to serve badge images on the web. See the Flask example for a minimal setup.
Start the example server with:
nox -s serve
Then open http://127.0.0.1:5000/ to view the badges.
Preset Badges
Common badge types with automatic color coding, no manual color picking needed:
| Command | Preview |
|---|---|
badgepy preset build passing -o badges/build.svg |
|
badgepy preset coverage 85.3 -o badges/coverage.svg |
|
badgepy preset version v1.2.3 -o badges/version.svg |
|
badgepy preset license MIT -o badges/license.svg |
|
badgepy preset custom "linux" --label platform --color green -o badges/platform.svg |
|
badgepy preset progress 75 --label docs -o badges/docs.svg |
From Python:
from badgepy.presets import build_badge, coverage_badge, custom_badge, progress_badge
svg = build_badge('passing')
svg = coverage_badge(85.3)
svg = custom_badge(label='platform', message='linux', color='green')
svg = progress_badge(75, label='docs')
CI Report Badges
Generate badges directly from test and coverage report files — no external API needed:
| Command | Preview |
|---|---|
badgepy from-junit tests/test-results.xml -o badges/tests.svg |
|
badgepy from-coverage tests/coverage.xml --output-dir badges/ |
# From generic key-value or JSON files
badgepy from-generic metrics.json --output-dir badges/
# From structured local JSON/TOML files
badgepy from-json package.json --query version --label npm -o badges/npm.svg
badgepy from-pyproject --query project.name --label package -o badges/package.svg
badgepy from-lock uv.lock jinja2 --label jinja2 -o badges/jinja2.svg
badgepy from-json coverage-summary.json \
--query total.lines.pct \
--label coverage \
--template "{value}%" \
--thresholds "90:brightgreen,80:green,60:yellow,0:red" \
-o badges/coverage.svg
From Python:
from badgepy.parsers import badges_from_junit, badges_from_coverage
from badgepy.parsers.structured import badge_from_structured_data
badges = badges_from_junit('tests/test-results.xml') # {'tests': '<svg...>'}
badges = badges_from_coverage('tests/coverage.xml') # {'coverage': '<svg...>', 'branch-coverage': '<svg...>'}
svg = badge_from_structured_data('pyproject.toml', query='project.version', label='pypi')
See the CI Integration Guide for GitHub Actions, GitLab CI, and Jenkins examples.
Output to File
Use -o / --output to write badges to a file instead of stdout:
badgepy --left-text=build --right-text=passing --right-color=green -o badges/build.svg
Caveats
-
Text measurement: badgepy uses a pre-calculated table of text widths and kerning distances for western glyphs. Eastern European languages may not render as accurately:
Glyphs not present in Deja Vu Sans (the default font) may render poorly:
-
Right-to-left languages: Arabic, Hebrew, and other RTL scripts are not explicitly supported; the text direction may be incorrect:
Development
git clone https://github.com/shenxianpeng/badgepy.git
cd badgepy
python -m venv venv
source venv/bin/activate
pip install -e .[dev]
nox
Contributions are welcome! Please read the contributor guide before submitting a PR.
Versioning
This project follows SemVer. See the changelog for what changed in each release, including upgrade notes for major versions.
License
This project is licensed under the Apache License — see the LICENSE file for details.
Metadata
Release files for badgepy 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| badgepy-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Release files / badgepy-2.0.0-py3-none-any.whl
| Download URL | badgepy-2.0.0-py3-none-any.whl |
|---|---|
| Size | 78.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0058abc6c29277f038c744d879f2125ec1e33bec5da99f82c0315331a9d1f006
|
|
BLAKE2b-256 checksum How to use checksums |
d46e0072e316be2a32244d892340808dc5e1c08f6bc8f67ef3803cd8d832c9cb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.8
|