Semantic versioning made easy for Python
Project description
🐊 Semmy
Semantic versioning made easy for Python.
Features
With semmy you can...
- Parse semantic version domain objects from valid strings
- Check if two versions are equal
- Check if version is greater (newer) or lesser (older) than other version
- Check if version is a pre-release
- Bump (pre-)major, (pre-)minor, and (pre-)patch versions
Prerequisites
- Python >=3.8 or later
Install
poetry add semmy
Alternatively, for older projects.
pip install semmy
pip freeze > requirements.txt
Usage
Below are the most common use cases. Please, check the unit tests for complete examples.
Importing
>>> from semmy import Semver
Initializing a raw object
Plain objects are easy to initialize given three semantic version components.
>>> Semver(1, 2, 3)
Version (1.2.3)
Keyword arguments are supported, too.
>>> Semver(major=1, minor=2, patch=3)
Version (1.2.3)
Versions may contain pre-release tag and build number.
>>> Semver(1, 0, 0, pre_release="rc-1")
Version (1.0.0-rc-1)
>>> Semver(1, 0, 0, build="6c231887917e472da7f299c934b20f29")
Version (1.0.0+6c231887917e472da7f299c934b20f29)
Initializing from string
You can pass a string and have it transformed to a valid object.
>>> Semver.from_string("1.0.0")
Version (1.0.0)
Exporting as tuple
Versions can be exported as integer tuples.
>>> Semver(1, 2, 3).as_tuple()
(1, 2, 3)
Validating input
I recommend using Semver.from_string() whenever possible as it includes a strict input validation.
For invalid inputs, instance of SemverException is raised, which should be caught.
>>> from semmy import Semver, SemverException
>>> try:
... Semver.from_string("not-a-version")
... except SemverException as e:
... print(e)
...
Version string not-a-version is not a valid semantic version
Comparing versions
Two versions are ordered by comparing their major, minor, and patch numbers respectively.
>>> Semver.from_string("1.2.3") == Semver(1, 2, 3)
True
>>> Semver.from_string("1.1.0") > Semver(1, 0, 0)
True
>>> Semver.from_string("0.9.0") < Semver(0, 9, 1)
True
You may also want to sort a list of versions where Python's tuple ordering is helpful.
>>> versions: list[Semver] = [
... Semver(1, 2, 3),
... Semver(2, 0, 0),
... Semver(0, 1, 0),
... ]
>>>
>>> sorted(versions, key=lambda v: v.as_tuple(), reverse=True)
[Version (2.0.0), Version (1.2.3), Version (0.1.0)]
Bumping versions
Typically, you want to bump major version for breaking changes, minor version for new features, and patch version for new fixes. These are supported.
>>> Semver(0, 1, 0).bump_major()
Version (1.0.0)
>>> Semver(1, 0, 0).bump_minor()
Version (1.1.0)
>>> Semver(1, 1, 0).bump_patch()
Version (1.1.1)
Contributing
See here for instructions.
Release Setup
This project uses automated releases with Release Please and GitHub Actions.
Required GitHub Repository Secrets
Before the release workflow can function properly, you need to configure the following secrets in your GitHub repository:
PYPI_API_TOKEN
Purpose: Authenticate with PyPI for publishing packages
Setup Instructions:
- Go to PyPI Account Settings
- Navigate to the "API tokens" section
- Click "Add API token"
- Set the token name (e.g., "semmy-github-actions")
- Set the scope to "Entire account" or specific to the "semmy" project
- Copy the generated token (it starts with
pypi-) - Add it to your GitHub repository secrets as
PYPI_API_TOKEN
Alternative: PyPI Trusted Publishing (Recommended)
Instead of using an API token, you can set up trusted publishing which is more secure:
- Go to your project on PyPI: https://pypi.org/manage/project/semmy/
- Navigate to "Publishing" → "Add a new pending publisher"
- Fill in the details:
- Owner:
nikoheikkila(your GitHub username) - Repository name:
semmy - Workflow name:
release.yml - Environment name: Leave empty (unless you use environments)
- Owner:
- Save the publisher
If you use trusted publishing, you can remove the UV_PUBLISH_TOKEN environment variable from the workflow.
How the Release Process Works
1. Release Please Phase
When you push commits to the main branch:
- Release Please analyzes conventional commit messages since the last release
- If release-worthy changes are found, it creates/updates a "Release PR"
- The Release PR contains:
- Updated version in
pyproject.toml - Updated
CHANGELOG.md - Any other version-related files
- Updated version in
2. Release Creation Phase
When you merge the Release PR:
- Release Please creates a GitHub Release and Git tag
- The workflow triggers the Publish job
- The publish job:
- Runs tests to ensure quality
- Builds the package with
uv build - Publishes to PyPI with
uv publish - Creates a workflow summary
Conventional Commit Types
The workflow recognizes these conventional commit types:
| Type | Description | Release Impact | Changelog Section |
|---|---|---|---|
feat |
New feature | Minor version bump | ✨ Features |
fix |
Bug fix | Patch version bump | 🐛 Bug Fixes |
perf |
Performance improvement | Patch version bump | ⚡ Performance Improvements |
revert |
Revert previous change | Patch version bump | ⏪ Reverts |
docs |
Documentation changes | Patch version bump | 📚 Documentation |
style |
Code style changes | Patch version bump | 🎨 Styles |
refactor |
Code refactoring | Patch version bump | ♻️ Code Refactoring |
test |
Test changes | Patch version bump | ✅ Tests |
build |
Build system changes | Patch version bump | 👷 Build System |
ci |
CI configuration changes | Patch version bump | 💚 Continuous Integration |
chore |
Maintenance tasks | No release | (Hidden from changelog) |
Breaking Changes
To trigger a major version bump, use:
feat!:orfix!:(with exclamation mark)- Include
BREAKING CHANGE:in the commit message body
Example Commits
# Patch release (1.0.0 → 1.0.1)
git commit -m "fix: resolve null pointer exception in parser"
# Minor release (1.0.0 → 1.1.0)
git commit -m "feat: add support for YAML configuration files"
# Major release (1.0.0 → 2.0.0)
git commit -m "feat!: remove deprecated API methods"
# Or with body:
git commit -m "feat: new API design
BREAKING CHANGE: The old API methods have been removed."
Manual Release
If you need to create a release manually:
# Create a commit that bumps the version
git commit -m "chore(release): 1.2.3" --allow-empty
# Push to main
git push origin main
Troubleshooting
Release PR not created
- Ensure your commits follow conventional commit format
- Check that you have release-worthy commit types (not just
chore) - Verify the workflow has proper permissions
PyPI publish fails
- Verify
PYPI_API_TOKENis correctly set - Ensure the package name isn't already taken by another user
- Check that the version doesn't already exist on PyPI
Tests fail during release
- All tests must pass before publishing
- Fix the failing tests and push the fix to main
- The workflow will retry on the next push
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file semmy-2.0.1.tar.gz.
File metadata
- Download URL: semmy-2.0.1.tar.gz
- Upload date:
- Size: 32.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e8bdee367ffc8356b4873ad3148040c0b76580ca32514bc86a11ee81ef5e114a
|
|
| MD5 |
a5d7b2ae8ca8e48f3fd0316ce01d546b
|
|
| BLAKE2b-256 |
016c47b0573d01f1fcd38190d2fe40b0c34fb1ba929c90a832807ba56d2901fa
|
File details
Details for the file semmy-2.0.1-py3-none-any.whl.
File metadata
- Download URL: semmy-2.0.1-py3-none-any.whl
- Upload date:
- Size: 6.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
84df4ec114c637b7ce9087700c9a5a7c8f0d89618c037be57867fbb5a8ddcc62
|
|
| MD5 |
c97c4e2845c8c2e4ac94dfce8cc6e1c1
|
|
| BLAKE2b-256 |
5a3090bedea8bc1838689cd873f033cb7f42357465b02f4664113fff3056b4c4
|