Skip to main content

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:

  1. Go to PyPI Account Settings
  2. Navigate to the "API tokens" section
  3. Click "Add API token"
  4. Set the token name (e.g., "semmy-github-actions")
  5. Set the scope to "Entire account" or specific to the "semmy" project
  6. Copy the generated token (it starts with pypi-)
  7. 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:

  1. Go to your project on PyPI: https://pypi.org/manage/project/semmy/
  2. Navigate to "Publishing" → "Add a new pending publisher"
  3. Fill in the details:
    • Owner: nikoheikkila (your GitHub username)
    • Repository name: semmy
    • Workflow name: release.yml
    • Environment name: Leave empty (unless you use environments)
  4. 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:

  1. Release Please analyzes conventional commit messages since the last release
  2. If release-worthy changes are found, it creates/updates a "Release PR"
  3. The Release PR contains:
    • Updated version in pyproject.toml
    • Updated CHANGELOG.md
    • Any other version-related files

2. Release Creation Phase

When you merge the Release PR:

  1. Release Please creates a GitHub Release and Git tag
  2. The workflow triggers the Publish job
  3. 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!: or fix!: (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_TOKEN is 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

semmy-2.0.1.tar.gz (32.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

semmy-2.0.1-py3-none-any.whl (6.7 kB view details)

Uploaded Python 3

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

Hashes for semmy-2.0.1.tar.gz
Algorithm Hash digest
SHA256 e8bdee367ffc8356b4873ad3148040c0b76580ca32514bc86a11ee81ef5e114a
MD5 a5d7b2ae8ca8e48f3fd0316ce01d546b
BLAKE2b-256 016c47b0573d01f1fcd38190d2fe40b0c34fb1ba929c90a832807ba56d2901fa

See more details on using hashes here.

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

Hashes for semmy-2.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 84df4ec114c637b7ce9087700c9a5a7c8f0d89618c037be57867fbb5a8ddcc62
MD5 c97c4e2845c8c2e4ac94dfce8cc6e1c1
BLAKE2b-256 5a3090bedea8bc1838689cd873f033cb7f42357465b02f4664113fff3056b4c4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page