Showing preview only (256K chars total). Download the full file or copy to clipboard to get everything.
Repository: k-bx/python-semver
Branch: master
Commit: cc5b1d370f74
Files: 108
Total size: 232.3 KB
Directory structure:
gitextract_lbii5c7k/
├── .coveragerc
├── .editorconfig
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yaml
│ │ ├── config.yml
│ │ └── feature_request.yaml
│ ├── dependabot.yml
│ └── workflows/
│ ├── codeql-analysis.yml
│ └── python-testing.yml
├── .gitignore
├── .pytest.ini
├── .readthedocs.yaml
├── .ruff.toml
├── CHANGELOG.rst
├── CITATION.cff
├── CONTRIBUTING.rst
├── CONTRIBUTORS
├── LICENSE.txt
├── MANIFEST.in
├── Makefile
├── README.rst
├── SUPPORT.md
├── changelog.d/
│ ├── .gitignore
│ ├── 460.bugfix.rst
│ ├── 463.trivial.rst
│ ├── README.rst
│ └── _template.rst
├── docs/
│ ├── Makefile
│ ├── _static/
│ │ └── css/
│ │ └── custom.css
│ ├── _templates/
│ │ └── layout.html
│ ├── advanced/
│ │ ├── coerce.py
│ │ ├── combine-pydantic-and-semver.rst
│ │ ├── convert-pypi-to-semver.rst
│ │ ├── create-subclasses-from-version.rst
│ │ ├── deal-with-invalid-versions.rst
│ │ ├── display-deprecation-warnings.rst
│ │ ├── index.rst
│ │ ├── semverwithvprefix.py
│ │ └── version-from-file.rst
│ ├── api.rst
│ ├── build-semver.rst
│ ├── changelog-semver3-devel.rst
│ ├── changelog.rst
│ ├── conf.py
│ ├── contribute/
│ │ ├── add-changelog-entry.rst
│ │ ├── doc-semver.rst
│ │ ├── finish-release.rst
│ │ ├── index.rst
│ │ ├── prerequisites.rst
│ │ ├── release-procedure.rst
│ │ ├── report-bugs.rst
│ │ └── run-test-suite.rst
│ ├── index.rst
│ ├── install.rst
│ ├── inventories/
│ │ ├── pydantic.inv
│ │ └── python-objects.inv
│ ├── make.bat
│ ├── migration/
│ │ ├── index.rst
│ │ ├── migratetosemver3.rst
│ │ └── replace-deprecated-functions.rst
│ ├── pysemver.rst
│ ├── readme.rst
│ ├── requirements.txt
│ ├── usage/
│ │ ├── access-parts-of-a-version.rst
│ │ ├── access-parts-through-index.rst
│ │ ├── check-compatible-semver-version.rst
│ │ ├── check-valid-semver-version.rst
│ │ ├── compare-versions-through-expression.rst
│ │ ├── compare-versions.rst
│ │ ├── convert-version-into-different-types.rst
│ │ ├── create-a-version.rst
│ │ ├── determine-version-equality.rst
│ │ ├── get-min-and-max-of-multiple-versions.rst
│ │ ├── increase-parts-of-a-version_prereleases.rst
│ │ ├── index.rst
│ │ ├── parse-version-string.rst
│ │ ├── raise-parts-of-a-version.rst
│ │ ├── replace-parts-of-a-version.rst
│ │ ├── semver-version.rst
│ │ └── semver_org-version.rst
│ └── version-policy.rst
├── pyproject.toml
├── release-procedure.md
├── setup.cfg
├── src/
│ └── semver/
│ ├── __about__.py
│ ├── __init__.py
│ ├── __main__.py
│ ├── _deprecated.py
│ ├── _types.py
│ ├── cli.py
│ ├── py.typed
│ └── version.py
├── tests/
│ ├── conftest.py
│ ├── test_bump.py
│ ├── test_compare.py
│ ├── test_deprecated_functions.py
│ ├── test_docstrings.py
│ ├── test_format.py
│ ├── test_immutable.py
│ ├── test_index.py
│ ├── test_match.py
│ ├── test_max-min.py
│ ├── test_parsing.py
│ ├── test_pysemver-cli.py
│ ├── test_replace.py
│ ├── test_semver.py
│ ├── test_subclass.py
│ └── test_typeerror-274.py
└── tox.ini
================================================
FILE CONTENTS
================================================
================================================
FILE: .coveragerc
================================================
# https://pytest-cov.readthedocs.io/
[run]
source = semver
branch = True
[report]
show_missing = true
precision = 1
exclude_lines =
pragma: no cover
if __name__ == .__main__.:
if not hasattr\(__builtins__, .cmp.\):
================================================
FILE: .editorconfig
================================================
# http://editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
max_line_length = 99
[*.py]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
[docs/Makefile]
indent_style = tab
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yaml
================================================
name: 🐞Bug report
description: Create a bug report to help us improve
# title: ''
labels: [bug]
assignees: [tomschr]
body:
- type: markdown
attributes:
value: |
Thanks a lot for taking the time to fill out this bug report! 🐛
It will help us to improve the project for everyone. 🌟
Find help from the community on our [GitHub Discussions](https://github.com/python-semver/python-semver/discussions) page or
on our [Documentation](https://python-semver.readthedocs.io/en/latest/).
- type: dropdown
id: python_version
attributes:
label: Which version of Python is the problem with?
multiple: true
options:
- "3.7"
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"
validations:
required: true
- type: input
id: semver_version
attributes:
label: What semver version are you using?
description: You can find this with `pip show semver`
placeholder: 3.0.2
- type: dropdown
id: os
attributes:
label: What OS are you using? (Add more in the Environment section)
multiple: true
options:
- Linux
- Windows
- macOS
- Other
- type: textarea
id: situation
attributes:
label: Situation
description: A clear and concise description of what the bug is.
placeholder: Describe the problem you see...
validations:
required: true
- type: textarea
id: reproduction_steps
attributes:
label: How to reproduce
description: |
Steps to reproduce the behavior:
1. Run '...'
2. Scroll down to '....'
3. See error
placeholder: Describe the steps to reproduce the issue...
validations:
required: true
- type: textarea
id: expected_behavior
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
placeholder: Describe the expected behavior...
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment
description: Optionally provide some more details about your environment.
placeholder: Describe your environment...
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
- name: Community Support
url: https://github.com/python-semver/python-semver/discussions
about: Ask and answer questions in our discussion forum.
- name: Documentation
url: https://python-semver.readthedocs.io/
about: Find more information in our documentation.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yaml
================================================
name: Feature request
description: Suggest an idea for this project
# title: ''
labels: [enhancement]
assignees: [tomschr]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this feature request!
- type: textarea
id: situation
attributes:
label: Situation
description: A clear and concise description of what the feature is. Ex. I'm always frustrated when [...]
placeholder: Describe the situation...
validations:
required: true
- type: textarea
id: expected_solution
attributes:
label: Expected solution
description: A clear and concise description of what you want to happen.
placeholder: Describe the expected solution...
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives
description: A clear and concise description of any alternative solutions or features you've considered.
placeholder: Describe any alternatives...
================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "pip" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
day: "friday"
labels:
- "enhancement"
commit-message:
prefix: "pip"
# Allow up to 10 open pull requests for pip dependencies
open-pull-requests-limit: 5
================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ maint/v2, release/* ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '50 16 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
================================================
FILE: .github/workflows/python-testing.yml
================================================
---
name: Python
# HINT: Sync this paths with the egrep in step check_files
on:
push:
branches: [ "master", "main" ]
paths:
- 'pyproject.toml'
- 'setup.cfg'
- '**.py'
- '.github/workflows/*.yml'
pull_request:
branches: [ "master", "main" ]
paths:
- 'pyproject.toml'
- 'setup.cfg'
- '**.py'
- '.github/workflows/*.yml'
permissions:
contents: read
concurrency:
# only cancel in-progress runs of the same workflow
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check-files:
runs-on: ubuntu-latest
outputs:
can_run: ${{ steps.check_files.outputs.can_run }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: GitHub variables
id: gh-vars
run: |
for var in GITHUB_WORKFLOW GITHUB_ACTION GITHUB_ACTIONS GITHUB_REPOSITORY GITHUB_EVEN_NAME GITHUB_EVENT_PATH GITHUB_WORKSPACE GITHUB_SHA GITHUB_REF GITHUB_HEAD_REF GITHUB_BASE_REF; do
echo "$var = ${!var}"
done
- name: Check for file changes
id: check_files
run: |
# ${{ github.event.after }} ${{ github.event.before }}
can_run=$(git diff --name-only HEAD~1 HEAD | \
egrep -q '.github/workflows/|pyproject.toml|setup.cfg|\.py$' && echo 1 || echo 0)
echo "can_run=$can_run"
echo "can_run=$can_run" >> $GITHUB_OUTPUT
skip_test:
runs-on: ubuntu-latest
needs: check-files
timeout-minutes: 2
if: ${{ needs.check-files.outputs.can_run == '0' }}
steps:
- name: Skip test
run: |
echo "Nothing to do as no TOML, Python, or YAML file has been changed.
"
echo "Skipping."
check:
runs-on: ubuntu-latest
needs: check-files
timeout-minutes: 15
# needs.check-files.outputs.can_run
if: ${{ needs.check-files.outputs.can_run == '1' }}
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
- name: Set up Python 3.7
# check tox.ini for the lowest version
run: uv python install 3.7
- name: Install the project
run: |
uv sync --all-extras --group gh-action
- name: Checks
continue-on-error: true
run: uv run tox run -e checks
tests:
needs: check
runs-on: ${{ matrix.os }}
# continue-on-error: true
strategy:
max-parallel: 5
fail-fast: true
matrix:
python-version: ["3.7",
"3.8",
"3.9",
"3.10",
"3.11",
"3.12",
"3.13",
]
os: ["ubuntu-latest", "macos-latest"]
exclude:
- os: "macos-latest"
python-version: "3.7"
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v3
- name: Set up Python ${{ matrix.python-version }}
run: uv python install ${{ matrix.python-version }}
- name: Install the project
run: |
uv sync --all-extras --dev
uv pip install tox tox-gh-actions
- name: Checks
run: uv run tox run -e ${{ matrix.python-version }}
================================================
FILE: .gitignore
================================================
# Python .gitignore file from gh://github/gitignore/Python.gitignore
#
# 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
# 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/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
#/-- Python
#--- Kate from gh://github/gitignore/Global/Kate.gitignore
*.kate-swp
.swp.*
#/--- Kate
#--- Vim from gh://github/gitignore/Global/Vim.gitignore
# Swap
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
Sessionx.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
#/--- Vim
#--- VisualStudioCode from gh://github/gitignore/Global/VisualStudioCode.gitignore
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
#/--- VisualStudio
#--- JetBrains from gh://github/gitignore/Global/JetBrains.gitignore
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
#/--- JetBrains
# --------
# Ignore files in the project's root:
/*.patch
/*.diff
/*.py
# but not this file:
!/setup.py
docs/_api
!docs/_api/semver.__about__.rst
# For node
node_modules/
================================================
FILE: .pytest.ini
================================================
[pytest]
norecursedirs = .git build .env/ env/ .pyenv/ .tmp/ .eggs/ venv/
testpaths = tests docs
pythonpath = src tests
filterwarnings =
ignore:Function 'semver.*:DeprecationWarning
# ' <- This apostroph is just to fix syntax highlighting
addopts =
--import-mode=importlib
--no-cov-on-fail
--cov=semver
--cov-report=term-missing
--doctest-glob='*.rst'
--doctest-modules
--doctest-report ndiff
================================================
FILE: .readthedocs.yaml
================================================
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.11"
jobs:
pre_install:
# - curl -LsSf https://astral.sh/uv/install.sh | sh
- pip install uv
- uv export --only-group docs --no-hashes --no-color > requirements-docs.txt
post_install:
- pip install -r requirements-docs.txt
pre_build:
- make -C docs html
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
================================================
FILE: .ruff.toml
================================================
#
# Configuration file for ruff
# See https://docs.astral.sh/ruff/configuration/
line-length = 88
indent-width = 4
include = [
"pyproject.toml",
"src/**/*.py",
"tests/**/*.py",
"docs/**/*.py",
]
[lint]
extend-ignore = [
"RUF005",
"RUF012",
# Comment contains ambiguous `’` (RIGHT SINGLE QUOTATION MARK):
"RUF003",
"ISC001",
]
select = [
"F", # pyflakes
"E", # pycodestyle
"I", # isort
"N", # pep8-naming
"UP", # pyupgrade
"RUF", # ruff
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"ISC", # flake8-implicit-str-concat
"PTH", # flake8-use-pathlib
"SIM", # flake8-simplify
"TID", # flake8-tidy-imports
]
# TODO: Remove ISC001 ignore when formatter updated: https://github.com/astral-sh/ruff/issues/8272
[format]
# Exclude type hint stub files from formatting.
exclude = ["*.pyi"]
# Like Black, use double quotes for strings.
quote-style = "double"
# Like Black, indent with spaces, rather than tabs.
indent-style = "space"
# Like Black, respect magic trailing commas.
skip-magic-trailing-comma = false
docstring-code-format = true
docstring-code-line-length = "dynamic"
================================================
FILE: CHANGELOG.rst
================================================
##########
Change Log
##########
Changes for the upcoming release can be found in
the `"changelog.d" directory <https://github.com/python-semver/python-semver/tree/master/changelog.d>`_
in our repository.
This section covers the changes between major version 2 and version 3.
..
Do *NOT* add changelog entries here!
This changelog is managed by towncrier and is compiled at release time.
See https://python-semver.rtd.io/en/latest/development.html#changelog
for details.
.. towncrier release notes start
Version 3.0.4
=============
:Released: 2025-01-24
:Maintainer: Tom Schraitle
Bug Fixes
---------
* :gh:`459`: Fix 3.0.3:
* :pr:`457`: Re-enable Trove license identifier
* :pr:`456`: Fix source dist file
----
Version 3.0.3
=============
:Released: 2025-01-18
:Maintainer: Tom Schraitle
Bug Fixes
---------
* :pr:`453`: The check in ``_comparator`` does not match the check in :meth:`Version.compare`.
This breaks comparision with subclasses.
Improved Documentation
----------------------
* :pr:`435`: Several small improvements for documentation:
* Add meta description to improve SEO
* Use canonicals on ReadTheDocs (commit 87f639f)
* Pin versions for reproducable doc builds (commit 03fb990)
* Add missing :file:`.readthedocs.yaml` file (commit ec9348a)
* Correct some smaller issues when building (commit f65feab)
* :pr:`436`: Move search box more at the top. This makes it easier for
users as if the TOC is long, the search box isn't visible
anymore.
Features
--------
* :pr:`439`: Improve type hints to fix TODOs
Internal Changes
----------------
* :pr:`440`: Update workflow file
* :pr:`446`: Add Python 3.13 to GitHub Actions
* :pr:`447`: Modernize project configs with :file:`pyproject.toml` and
use Astral's uv command.
* In :file:`pyproject.toml`:
* Move all project related data from :file:`setup.cfg` to :file:`pyproject.toml`
* Use new dependency group from :pep:`735`
* Consolidate flake8, isort, pycodestyle with ruff
* Split towncrier config type "trivial" into "trivial" and "internal"
* Create config file for ruff (:file:`.ruff.toml`)
* Create config file for pytest (:file:`.pytest.ini`)
* Simplify :file:`tox.ini` and remove old stuff
* Document installation with new :command:`uv` command
* Simplify Sphinx config with :func:`find_version()`
* Update the authors
* Use :command:`uv` in GitHub Action :file:`python-testing.yml` workflow
* Update :file:`release-procedure.md`.
* :pr:`451`: Turn our Markdown issue templates into YAML
Trivial Changes
---------------
* :pr:`438`: Replace organization placeholder in license
* :pr:`445`: Improve private :func:`_nat_cmp` method:
* Remove obsolete else.
* Find a better way to identify digits without the :mod:`re` module.
* Fix docstring in :meth:`Version.compare`
----
Version 3.0.2
=============
:Released: 2023-10-09
:Maintainer: Tom Schraitle
Bug Fixes
---------
* :pr:`418`: Replace :class:`~collection.OrderedDict` with :class:`dict`.
The dict datatype is ordered since Python 3.7. As we do not support
Python 3.6 anymore, it can be considered safe to avoid :class:`~collection.OrderedDict`.
Related to :gh:`419`.
* :pr:`426`: Fix :meth:`~semver.version.Version.replace` method to use the derived class
of an instance instead of :class:`~semver.version.Version` class.
Improved Documentation
----------------------
* :pr:`431`: Clarify version policy for the different semver versions (v2, v3, >v3)
and the supported Python versions.
* :gh:`432`: Improve external doc links to Python and Pydantic.
Features
--------
* :pr:`417`: Amend GitHub Actions to check against MacOS.
Trivial/Internal Changes
------------------------
* :pr:`420`: Introduce :py:class:`~typing.ClassVar` for some :class:`~semver.version.Version`
class variables, mainly :data:`~semver.version.Version.NAMES` and some private.
* :pr:`421`: Insert mypy configuration into :file:`pyproject.toml` and remove
config options from :file:`tox.ini`.
----
Version 3.0.1
=============
:Released: 2023-06-14
:Maintainer: Tom Schraitle
Bug Fixes
---------
* :gh:`410`: Export functions properly using ``__all__`` in ``__init__.py``.
----
Version 3.0.0
=============
:Released: 2023-04-02
:Maintainer: Tom Schraitle
Bug Fixes
---------
* :gh:`291`: Disallow negative numbers in VersionInfo arguments
for ``major``, ``minor``, and ``patch``.
* :gh:`310`: Rework API documentation.
Follow a more "semi-manual" attempt and add auto directives
into :file:`docs/api.rst`.
* :gh:`344`: Allow empty string, a string with a prefix, or ``None``
as token in
:meth:`~semver.version.Version.bump_build` and
:meth:`~semver.version.Version.bump_prerelease`.
* :gh:`374`: Correct Towncrier's config entries in the :file:`pyproject.toml` file.
The old entries ``[[tool.towncrier.type]]`` are deprecated and need
to be replaced by ``[tool.towncrier.fragment.<TYPE>]``.
* :pr:`384`: General cleanup, reformat files:
* Reformat source code with black again as some config options
did accidentely exclude the semver source code.
Mostly remove some includes/excludes in the black config.
* Integrate concurrency in GH Action
* Ignore Python files on project dirs in .gitignore
* Remove unused patterns in MANIFEST.in
* Use ``extend-exclude`` for flake in :file:`setup.cfg`` and adapt list.
* Use ``skip_install=True`` in :file:`tox.ini` for black
* :pr:`393`: Fix command :command:`python -m semver` to avoid the error "invalid choice"
* :pr:`396`: Calling :meth:`~semver.version.Version.parse` on a derived class will show correct type of derived class.
Deprecations
------------
* :gh:`169`: Deprecate CLI functions not imported from ``semver.cli``.
* :gh:`234`: In :file:`setup.py` simplified file and remove
``Tox`` and ``Clean`` classes
* :gh:`284`: Deprecate the use of :meth:`~Version.isvalid`.
Rename :meth:`~semver.version.Version.isvalid`
to :meth:`~semver.version.Version.is_valid`
for consistency reasons with :meth:`~semver.version.Version.is_compatible`.
* :pr:`290`: For semver 3.0.0-alpha0 deprecated:
* Remove anything related to Python2
* In :file:`tox.ini` and :file:`.travis.yml`
Remove targets py27, py34, py35, and pypy.
Add py38, py39, and nightly (allow to fail)
* In :file:`setup.py` simplified file and remove
``Tox`` and ``Clean`` classes
* Remove old Python versions (2.7, 3.4, 3.5, and pypy)
from Travis
* :gh:`372`: Deprecate support for Python 3.6.
Python 3.6 reached its end of life and isn't supported anymore.
At the time of writing (Dec 2022), the lowest version is 3.7.
Although the `poll <https://github.com/python-semver/python-semver/discussions/371>`_
didn't cast many votes, the majority agreed to remove support for
Python 3.6.
* :pr:`402`: Keep :func:`semver.compare <semver._deprecated.compare>`.
Although it breaks consistency with module level functions, it seems it's
a much needed/used function. It's still unclear if we should deprecate
this function or not (that's why we use :py:exc:`PendingDeprecationWarning`).
As we don't have a uniform initializer yet, this function stays in the
:file:`_deprecated.py` file for the time being until we find a better solution. See :gh:`258` for details.
Features
--------
* :gh:`169`: Create semver package and split code among different modules in the packages:
* Remove :file:`semver.py`
* Create :file:`src/semver/__init__.py`
* Create :file:`src/semver/cli.py` for all CLI methods
* Create :file:`src/semver/_deprecated.py` for the ``deprecated`` decorator and other deprecated functions
* Create :file:`src/semver/__main__.py` to allow calling the CLI using :command:`python -m semver`
* Create :file:`src/semver/_types.py` to hold type aliases
* Create :file:`src/semver/version.py` to hold the :class:`Version` class (old name :class:`VersionInfo`) and its utility functions
* Create :file:`src/semver/__about__.py` for all the metadata variables
* :gh:`213`: Add typing information
* :gh:`284`: Implement :meth:`~semver.version.Version.is_compatible` to make "is self compatible with X".
* :gh:`305`: Rename :class:`~semver.version.VersionInfo` to :class:`~semver.version.Version` but keep an alias for compatibility
* :pr:`359`: Add optional parameter ``optional_minor_and_patch`` in :meth:`~semver.version.Version.parse` to allow optional
minor and patch parts.
* :pr:`362`: Make :meth:`~semver.version.Version.match` accept a bare version string as match expression, defaulting to equality testing.
* :gh:`364`: Enhance :file:`pyproject.toml` to make it possible to use the
:command:`pyproject-build` command from the build module.
For more information, see :ref:`build-semver`.
* :gh:`365`: Improve :file:`pyproject.toml`.
* Use setuptools, add metadata. Taken approach from
`A Practical Guide to Setuptools and Pyproject.toml
<https://godatadriven.com/blog/a-practical-guide-to-setuptools-and-pyproject-toml/>`_.
* Doc: Describe building of semver
* Remove :file:`.travis.yml` in :file:`MANIFEST.in`
(not needed anymore)
* Distinguish between Python 3.6 and others in :file:`tox.ini`
* Add skip_missing_interpreters option for :file:`tox.ini`
* GH Action: Upgrade setuptools and setuptools-scm and test
against 3.11.0-rc.2
Improved Documentation
----------------------
* :gh:`276`: Document how to create a sublass from :class:`~semver.version.VersionInfo` class
* :gh:`284`: Document deprecation of :meth:`~semver.version.Version.isvalid`.
* :pr:`290`: Several improvements in the documentation:
* New layout to distinguish from the semver2 development line.
* Create new logo.
* Remove any occurances of Python2.
* Describe changelog process with Towncrier.
* Update the release process.
* :gh:`304`: Several improvements in documentation:
* Reorganize API documentation.
* Add migration chapter from semver2 to semver3.
* Distinguish between changlog for version 2 and 3
* :gh:`305`: Add note about :class:`~semver.version.Version` rename.
* :gh:`312`: Rework "Usage" section.
* Mention the rename of :class:`~semver.version.VersionInfo` to
:class:`~semver.version.Version` class
* Remove semver. prefix in doctests to make examples shorter
* Correct some references to dunder methods like
:func:`~semver.version.Version.__getitem__`,
:func:`~semver.version.Version.__gt__` etc.
* Remove inconsistencies and mention module level function as
deprecated and discouraged from using
* Make empty :py:func:`super` call in :file:`semverwithvprefix.py` example
* :gh:`315`: Improve release procedure text
* :gh:`335`: Add new section "Converting versions between PyPI and semver" the limitations
and possible use cases to convert from one into the other versioning scheme.
* :gh:`340`: Describe how to get version from a file
* :gh:`343`: Describe combining Pydantic with semver in the "Advanced topic"
section.
* :gh:`350`: Restructure usage section. Create subdirectory "usage/" and splitted
all section into different files.
* :gh:`351`: Introduce new topics for:
* "Migration to semver3"
* "Advanced topics"
* :pr:`392`: Fix the example in the documentation for combining semver and pydantic.
Trivial/Internal Changes
------------------------
* :gh:`169`: Adapted infrastructure code to the new project layout.
* Replace :file:`setup.py` with :file:`setup.cfg` because the :file:`setup.cfg` is easier to use
* Adapt documentation code snippets where needed
* Adapt tests
* Changed the ``deprecated`` to hardcode the ``semver`` package name in the warning.
Increase coverage to 100% for all non-deprecated APIs
* :pr:`290`: Add supported Python versions to :command:`black`.
* :gh:`304`: Support PEP-561 :file:`py.typed`.
According to the mentioned PEP:
"Package maintainers who wish to support type checking
of their code MUST add a marker file named :file:`py.typed`
to their package supporting typing."
Add package_data to :file:`setup.cfg` to include this marker in dist
and whl file.
* :gh:`309`: Some (private) functions from the :mod:`semver.version`
module has been changed.
The following functions got renamed:
* function :func:`semver.version.comparator` got renamed to
:func:`semver.version._comparator` as it is only useful
inside the :class:`~semver.version.Version` class.
* function :func:`semver.version.cmp` got renamed to
:func:`semver.version._cmp` as it is only useful
inside the :class:`~semver.version.Version` class.
The following functions got integrated into the
:class:`~semver.version.Version` class:
* function :func:`semver.version._nat_cmd` as a classmethod
* function :func:`semver.version.ensure_str`
* :gh:`313`: Correct :file:`tox.ini` for ``changelog`` entry to skip
installation for semver. This should speed up the execution
of towncrier.
* :gh:`316`: Comparisons of :class:`~semver.version.Version` class and other
types return now a :py:const:`NotImplemented` constant instead
of a :py:exc:`TypeError` exception.
The `NotImplemented`_ section of the Python documentation recommends
returning this constant when comparing with ``__gt__``, ``__lt__``,
and other comparison operators to "to indicate that the operation is
not implemented with respect to the other type".
.. _NotImplemented: https://docs.python.org/3/library/constants.html#NotImplemented
* :gh:`319`: Introduce stages in :file:`.travis.yml`
The config file contains now two stages: check and test. If
check fails, the test stage won't be executed. This could
speed up things when some checks fails.
* :gh:`322`: Switch from Travis CI to GitHub Actions.
* :gh:`347`: Support Python 3.10 in GitHub Action and other config files.
* :gh:`378`: Fix some typos in Towncrier configuration
* :gh:`388`: For pytest, switch to the more modern :mod:`importlib` approach
as it doesn't require to modify :data:`sys.path`:
https://docs.pytest.org/en/7.2.x/explanation/pythonpath.html
* :pr:`389`: Add public class variable :data:`Version.NAMES <semver.version.Version.NAMES>`.
This class variable contains a tuple of strings that contains the names of
all attributes of a Version (like ``"major"``, ``"minor"`` etc).
In cases we need to have dynamical values, this makes it easier to iterate.
..
Local variables:
coding: utf-8
mode: text
mode: rst
End:
vim: fileencoding=utf-8 filetype=rst :
================================================
FILE: CITATION.cff
================================================
# This CITATION.cff file was generated with cffinit:
# https://bit.ly/cffinit
cff-version: 1.2.0
title: python-semver
message: >-
If you use this software, please cite it using the
metadata from this file.
type: software
authors:
- given-names: Kostiantyn
family-names: Rybnikov
email: k-bx@k-bx.com
- given-names: Tom
family-names: Schraitle
email: tom_schr@web.de
- given-names: Sebastian
family-names: Celles
email: s.celles@gmail.com
- name: "The python-semver software team"
identifiers:
- type: url
value: 'https://github.com/python-semver/python-semver'
description: GitHub python-semver/python-semver
url: 'https://python-semver.readthedocs.io'
repository-code: 'https://github.com/python-semver/python-semver'
repository-artifact: 'https://pypi.org/project/semver/'
abstract: >-
A Python module for semantic versioning. Simplifies
comparing versions. This modules follows the
MAJOR.MINOR.PATCH style.
keywords:
- Python
- Python module
- semver
- versioning
- semantic versioning
- semver-format
- semver-tag
- versions
references:
- authors:
- family-names: Preston-Werner
given-names: Tom
- name: "The semver team"
title: 'Semantic Versioning 2.0.0'
url: 'https://semver.org'
repository-code: 'https://github.com/semver/semver'
type: standard
version: 2.0.0
languages:
- ar
- bg
- ca
- cs
- da
- de
- el
- en
- es
- fa
- fr
- he
- hin
- hr
- hu
- hy
- id
- it
- ja
- ka
- kab
- ko
- nl
- pl
- pt
- ro
- ru
- sk
- sl
- sr
- sv
- tr
- uk
- vi
- zh
abstract: >-
Given a version number MAJOR.MINOR.PATCH, increment the:
1. MAJOR version when you make incompatible API changes
2. MINOR version when you add functionality in a backwards compatible manner
3. PATCH version when you make backwards compatible bug fixes
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
license: BSD-3-Clause
commit: 3a7680dc436211227c0aeae84c9b45e0b3345b8f
version: 3.0.0
date-released: '2023-04-02'
================================================
FILE: CONTRIBUTING.rst
================================================
.. _contributing:
Contributing to semver
======================
The semver source code is managed using Git and is hosted on GitHub::
git clone git://github.com/python-semver/python-semver
Reporting Bugs and Asking Questions
-----------------------------------
If you think you have encountered a bug in semver or have an idea for a new
feature? Great! We like to hear from you!
There are several options to participate:
* Open a new topic on our `GitHub discussion <gh_discussions>`_ page.
Tell us our ideas or ask your questions.
* Look into our GitHub `issues`_ tracker or open a new issue.
Prerequisites
-------------
Before you make changes to the code, we would highly appreciate if you
consider the following general requirements:
* Make sure your code adheres to the `Semantic Versioning`_ specification.
* Check if your feature is covered by the Semantic Versioning specification.
If not, ask on its GitHub project https://github.com/semver/semver.
More topics
-----------
* `Running the Test Suite <docs/contribute/run-test-suite.rst>`_
* `Documenting semver <docs/contribute/doc-semver.rst>`_
* `Adding a Changelog Entry <docs/contribute/add-changelog-entry.rst>`_
* `Preparing the Release <docs/contribute/release-procedure.rst>`_
* `Finish the Release <docs/contribute/finish-release.rst>`_
.. _black: https://black.rtfd.io
.. _docformatter: https://pypi.org/project/docformatter/
.. _flake8: https://flake8.rtfd.io
.. _mypy: http://mypy-lang.org/
.. _issues: https://github.com/python-semver/python-semver/issues
.. _pull request: https://github.com/python-semver/python-semver/pulls
.. _pytest: http://pytest.org/
.. _Semantic Versioning: https://semver.org
.. _Sphinx style: https://sphinx-rtd-tutorial.rtfd.io/en/latest/docstrings.html
.. _tox: https://tox.rtfd.org/
.. _gh_discussions: https://github.com/python-semver/python-semver/discussions
================================================
FILE: CONTRIBUTORS
================================================
############
Contributors
############
Python Semver library
#####################
This document records the primary maintainers and significant
contributors to this code base.
Thank you to everyone whose work has made this possible.
Primary maintainers
===================
* Tom Schraitle <tom_schr@web.de>
* Sébastien Celles <s.celles@gmail.com>
Old maintainer:
* Kostiantyn Rybnikov <k-bx@k-bx.com>
List of Contributors
====================
(in alphabetical order)
* Jelo Agnasin <jelo@icannhas.com>
* Carles Barrobés <carles@barrobes.com>
* Eli Bishop <eli-darkly@users.noreply.github.com>
* Peter Bittner <django@bittner.it>
* Craig Blaszczyk <masterjakul@gmail.com>
* Tyler Cross <tyler@crosscollab.com>
* Dennis Felsing <def-@users.noreply.github.com>
* Ben Finney <ben+python@benfinney.id.au>
* Zane Geiger <zanecodes@users.noreply.github.com>
* T. Jameson Little <t.jameson.little@gmail.com>
* Raphael Krupinski <rafalkrupinski@users.noreply.github.com>
* Thomas Laferriere <tlaferriere@users.noreply.github.com>
* Zack Lalanne <zack.lalanne@gmail.com>
* Tuure Laurinolli <tuure@laurinolli.net>
* Damien Nadé <anvil@users.noreply.github.com>
* Jan Pieter Waagmeester <jieter@jieter.nl>
* Alexander Puzynia <werwolf.by@gmail.com>
* Lexi Robinson <Lexicality@users.noreply.github.com>
* robi-wan <robi-wan@suyu.de>
* George Sakkis <gsakkis@users.noreply.github.com>
* Mike Salvatore <mssalvatore@users.noreply.github.com>
* sbrudenell <sbrudenell@users.noreply.github.com>
* Alexander Shorin <kxepal@gmail.com>
* Anton Talevnin <TalAntR@users.noreply.github.com>
* Karol Werner <karol.werner@ppkt.eu>
..
Local variables:
coding: utf-8
mode: text
mode: rst
End:
vim: fileencoding=utf-8 filetype=rst :
================================================
FILE: LICENSE.txt
================================================
Copyright (c) 2013, Konstantine Rybnikov
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
Neither the name of the python-semver org nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: MANIFEST.in
================================================
include *.md
include *.rst
include *.txt
include CONTRIBUTORS
include CITATION.cff
include Makefile
include changelog.d/*
graft docs/**
include tests/*.py
include tox.ini
include .pytest.ini
include .ruff.toml
include uv.lock
# The dot files:
include .coveragerc
include .editorconfig
include .gitignore
include .readthedocs.yaml
prune docs/_build
recursive-exclude .github *
prune docs/**/__pycache__
global-exclude __pycache__
================================================
FILE: Makefile
================================================
sdist:
python setup.py sdist bdist_wheel --universal
.PHONY sdist
================================================
FILE: README.rst
================================================
Quickstart
==========
.. teaser-begin
A Python module to simplify `semantic versioning`_.
|GHAction| |python-support| |downloads| |license| |docs| |black|
|openissues| |GHDiscussion|
.. teaser-end
The module follows the ``MAJOR.MINOR.PATCH`` style:
* ``MAJOR`` version when you make incompatible API changes,
* ``MINOR`` version when you add functionality in a backwards compatible manner, and
* ``PATCH`` version when you make backwards compatible bug fixes.
Additional labels for pre-release and build metadata are supported.
To import this library, use:
.. code-block:: python
>>> import semver
Working with the library is quite straightforward. To turn a version string into the
different parts, use the ``semver.Version.parse`` function:
.. code-block:: python
>>> ver = semver.Version.parse('1.2.3-pre.2+build.4')
>>> ver.major
1
>>> ver.minor
2
>>> ver.patch
3
>>> ver.prerelease
'pre.2'
>>> ver.build
'build.4'
To raise parts of a version, there are a couple of functions available for
you. The function ``semver.Version.bump_major`` leaves the original object untouched, but
returns a new ``semver.Version`` instance with the raised major part:
.. code-block:: python
>>> ver = semver.Version.parse("3.4.5")
>>> ver.bump_major()
Version(major=4, minor=0, patch=0, prerelease=None, build=None)
It is allowed to concatenate different "bump functions":
.. code-block:: python
>>> ver.bump_major().bump_minor()
Version(major=4, minor=1, patch=0, prerelease=None, build=None)
To compare two versions, semver provides the ``semver.compare`` function.
The return value indicates the relationship between the first and second
version:
.. code-block:: python
>>> semver.compare("1.0.0", "2.0.0")
-1
>>> semver.compare("2.0.0", "1.0.0")
1
>>> semver.compare("2.0.0", "2.0.0")
0
There are other functions to discover. Read on!
.. |latest-version| image:: https://img.shields.io/pypi/v/semver.svg
:alt: Latest version on PyPI
:target: https://pypi.org/project/semver
.. |python-support| image:: https://img.shields.io/pypi/pyversions/semver.svg
:target: https://pypi.org/project/semver
:alt: Python versions
.. |downloads| image:: https://img.shields.io/pypi/dm/semver.svg
:alt: Monthly downloads from PyPI
:target: https://pypi.org/project/semver
.. |license| image:: https://img.shields.io/pypi/l/semver.svg
:alt: Software license
:target: https://github.com/python-semver/python-semver/blob/master/LICENSE.txt
.. |docs| image:: https://readthedocs.org/projects/python-semver/badge/?version=latest
:target: http://python-semver.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
.. _semantic versioning: https://semver.org/
.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
:alt: Black Formatter
.. |Gitter| image:: https://badges.gitter.im/python-semver/community.svg
:target: https://gitter.im/python-semver/community
:alt: Gitter
.. |openissues| image:: http://isitmaintained.com/badge/open/python-semver/python-semver.svg
:target: http://isitmaintained.com/project/python-semver/python-semver
:alt: Percentage of open issues
.. |GHAction| image:: https://github.com/python-semver/python-semver/workflows/Python/badge.svg
:alt: Python
.. |GHDiscussion| image:: https://shields.io/badge/GitHub-%20Discussions-green?logo=github
:target: https://github.com/python-semver/python-semver/discussions
:alt: GitHub Discussion
================================================
FILE: SUPPORT.md
================================================
# Getting support
If you need help, try these ways:
* Ask your questions on the [discussion](https://github.com/python-semver/python-semver/discussions) page.
Our forum is a good way to post your questions.
* Read the [python-semver documentation](https://python-semver.readthedocs.io/).
The documentation contains all the information to install and use the library.
* Suggest a new feature or a new [issue](https://github.com/python-semver/python-semver/issues/new)
If you found a problem or would like to suggest a new feature that could improve python-semver,
this is the place to go.
================================================
FILE: changelog.d/.gitignore
================================================
!.gitignore
================================================
FILE: changelog.d/460.bugfix.rst
================================================
:meth:`~semver.version.Version.bump_prerelease` will now add `.0` to an
existing prerelease when the last segment of the current prerelease, split by
dots (`.`), is not numeric. This is to ensure the new prerelease is considered
higher than the previous one.
:meth:`~semver.version.Version.bump_prerelease` now also support an argument
`bump_when_empty` which will bump the patch version if there is no existing
prerelease, to ensure the resulting version is considered a higher version than
the previous one.
================================================
FILE: changelog.d/463.trivial.rst
================================================
Remove double code in :meth:`Version.bump_build`
================================================
FILE: changelog.d/README.rst
================================================
The ``changelog.d`` Directory
=============================
.. This file is also included into the documentation
.. -text-begin-
A "Changelog" is a record of all notable changes made to a project. Such
a changelog, in our case the :file:`CHANGELOG.rst`, is read by our *users*.
Therefor, any description should be aimed to users instead of describing
internal changes which are only relevant to developers.
To avoid merge conflicts, we use the `Towncrier`_ package to manage our changelog.
The directory :file:`changelog.d` contains "newsfragments" which are short
ReST-formatted files.
On release, those news fragments are compiled into our :file:`CHANGELOG.rst`.
You don't need to install ``towncrier`` yourself, use the :command:`tox` command
to call the tool.
We recommend to follow the steps to make a smooth integration of your changes:
#. After you have created a new pull request (PR), add a new file into the
directory :file:`changelog.d`. Each filename follows the syntax::
<ISSUE>.<TYPE>.rst
where ``<ISSUE>`` is the GitHub issue number.
In case you have no issue but a pull request, prefix your number with ``pr``.
``<TYPE>`` is one of:
* ``bugfix``: fixes a reported bug.
* ``deprecation``: informs about deprecation warnings
* ``doc``: improves documentation.
* ``feature``: adds new user facing features.
* ``removal``: removes obsolete or deprecated features.
* ``trivial``: fixes a small typo or internal change that might be noteworthy.
For example: ``123.feature.rst``, ``pr233.removal.rst``, ``456.bugfix.rst`` etc.
#. Create the new file with the command::
tox -e changelog -- create 123.feature.rst
The file is created int the :file:`changelog.d/` directory.
#. Open the file and describe your changes in RST format.
* Wrap symbols like modules, functions, or classes into double backticks
so they are rendered in a ``monospace font``.
* Prefer simple past tense or constructions with "now".
#. Check your changes with::
tox -e changelog -- check
#. Optionally, build a draft version of the changelog file with the command::
tox -e changelog
#. Commit all your changes and push it.
This finishes your steps.
On release, the maintainer compiles a new :file:`CHANGELOG.rst` file by running::
tox -e changelog -- build
This will remove all newsfragments inside the :file:`changelog.d` directory,
making it ready for the next release.
.. _Towncrier: https://pypi.org/project/towncrier
================================================
FILE: changelog.d/_template.rst
================================================
{% for section, _ in sections.items() %}
{% set underline = underlines[0] %}{% if section %}{{section}}
{{ underline * section|length }}{% set underline = underlines[1] %}
{% endif %}
:Released: {{ versiondata.date }}
:Maintainer:
{% if sections[section] %}
{% for category, val in definitions.items() if category in sections[section] %}
{{ definitions[category]['name'] }}
{{ underline * definitions[category]['name']|length }}
{% if definitions[category]['showcontent'] %}
{% for text, values in sections[section][category].items() %}
{%- for value in values %}
{% if value.startswith("pr") %}
* :pr:`{{ value[2:] }}`{% else %}
* :gh:`{{ value[1:] }}`{% endif %}{%- endfor -%}: {{ text }}
{% endfor %}
{% else %}
- {{ sections[section][category]['']|join(', ') }}
{% endif %}
{% if sections[section][category]|length == 0 %}
No significant changes.
{% else %}
{% endif %}
{% endfor %}
{% else %}
No significant changes.
{% endif %}
{% endfor %}
----
================================================
FILE: docs/Makefile
================================================
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = semver
SOURCEDIR = .
BUILDDIR = _build
# Set the source directory for your project
SRC_DIR = ../src
# Set the PYTHONPATH environment variable
export PYTHONPATH := $(SRC_DIR):$(PYTHONPATH)
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
================================================
FILE: docs/_static/css/custom.css
================================================
/*
https://github.com/bitprophet/alabaster
*/
/* Roboto (Sans), Roboto Slab ("serif"), Roboto Mono*/
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,400;0,600;1,400&family=Roboto+Slab:wght@700&family=Roboto:ital@0;1&display=swap');
.logo {
font-family: "Roboto Slab";
}
img.logo {
width: 80%;
}
div.document {
margin-top: 0pt;
}
div.related.top {
margin-top: -1em;
}
div.related.top nav {
margin-bottom: 0.5em;
margin-top: 0.5em;
}
.sphinxsidebarwrapper .caption {
margin-top: 1em;
margin-bottom: -0.75em;
}
#searchbox {
margin-bottom: 1.25em;
}
.section h1 {
font-weight: 700;
}
.py.class {
margin-top: 1.5em;
}
.py.method {
padding-top: 0.25em;
padding-bottom: 1.25em;
border-top: 1px solid #EEE;
}
.py.function{
padding-top: 1.25em;
}
.related.bottom {
margin-top: 1em;
}
body {
font-weight: 400;
}
nav#rellinks {
float: left;
width: 100%;
}
nav#rellinks li:first-child {
float: left;
text-align: left;
width: 50%;
}
nav#rellinks li:last-child {
float: right;
text-align: right;
width: 50%;
}
nav#rellinks li+li:before {
content: "";
}
div.related.top nav::after {
float: none;
}
================================================
FILE: docs/_templates/layout.html
================================================
{#
Import the theme's layout.
#}
{% extends "!layout.html" %}
{%- block extrahead %}
{{- super() }}
<meta name="generator" content="Toms"/>
{% endblock %}
================================================
FILE: docs/advanced/coerce.py
================================================
import re
from semver import Version
from typing import Optional, Tuple
BASEVERSION = re.compile(
r"""[vV]?
(?P<major>0|[1-9]\d*)
(\.
(?P<minor>0|[1-9]\d*)
(\.
(?P<patch>0|[1-9]\d*)
)?
)?
""",
re.VERBOSE,
)
def coerce(version: str) -> Tuple[Version, Optional[str]]:
"""
Convert an incomplete version string into a semver-compatible Version
object
* Tries to detect a "basic" version string (``major.minor.patch``).
* If not enough components can be found, missing components are
set to zero to obtain a valid semver version.
:param str version: the version string to convert
:return: a tuple with a :class:`Version` instance (or ``None``
if it's not a version) and the rest of the string which doesn't
belong to a basic version.
:rtype: tuple(:class:`Version` | None, str)
"""
match = BASEVERSION.search(version)
if not match:
return (None, version)
ver = {
key: 0 if value is None else value for key, value in match.groupdict().items()
}
ver = Version(**ver)
rest = match.string[match.end() :] # noqa:E203
return ver, rest
================================================
FILE: docs/advanced/combine-pydantic-and-semver.rst
================================================
Combining Pydantic and semver
=============================
.. meta::
:description lang=en:
Combining Pydantic and semver
According to its homepage, `Pydantic <https://pydantic-docs.helpmanual.io>`_
"enforces type hints at runtime, and provides user friendly errors when data
is invalid."
If you are working with Pydantic>2.0 and pydantic-extra-types>=2.10.5 use the built in `SemanticVersion` type, which wraps the :class:`Version <semver.version.Version>` class.
.. code-block:: python
from pydantic import BaseModel
from pydantic_extra_types.semantic_version import SemanticVersion
class appVersion(BaseModel):
version: SemanticVersion
app_version = appVersion(version="1.2.3")
print(app_version.version)
# > 1.2.3
To work with Pydantic>2.0 and without pydantic-extra-types use the following example to define your own type:
1. Derive a new class from :class:`~semver.version.Version`
first and add the magic methods :py:meth:`__get_pydantic_core_schema__`
and :py:meth:`__get_pydantic_json_schema__` like this:
.. code-block:: python
from typing import Annotated, Any, Callable
from pydantic import GetJsonSchemaHandler
from pydantic_core import core_schema
from pydantic.json_schema import JsonSchemaValue
from semver import Version
class _VersionPydanticAnnotation:
@classmethod
def __get_pydantic_core_schema__(
cls,
_source_type: Any,
_handler: Callable[[Any], core_schema.CoreSchema],
) -> core_schema.CoreSchema:
def validate_from_str(value: str) -> Version:
return Version.parse(value)
from_str_schema = core_schema.chain_schema(
[
core_schema.str_schema(),
core_schema.no_info_plain_validator_function(validate_from_str),
]
)
return core_schema.json_or_python_schema(
json_schema=from_str_schema,
python_schema=core_schema.union_schema(
[
core_schema.is_instance_schema(Version),
from_str_schema,
]
),
serialization = core_schema.to_string_ser_schema(),
)
@classmethod
def __get_pydantic_json_schema__(
cls, _core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
) -> JsonSchemaValue:
return handler(core_schema.str_schema())
ManifestVersion = Annotated[Version, _VersionPydanticAnnotation]
2. Create a new model (in this example :class:`MyModel`) and derive
it from :py:class:`pydantic:pydantic.BaseModel`:
.. code-block:: python
import pydantic
class MyModel(pydantic.BaseModel):
version: _VersionPydanticAnnotation
3. Use your model like this:
.. code-block:: python
model = MyModel.parse_obj({"version": "1.2.3"})
The attribute :py:attr:`model.version` will be an instance of
:class:`~semver.version.Version`.
If the version is invalid, the construction will raise a
:py:class:`pydantic:pydantic_core.ValidationError`.
================================================
FILE: docs/advanced/convert-pypi-to-semver.rst
================================================
Converting Versions between PyPI and semver
===========================================
.. meta::
:description lang=en:
Converting versions between PyPI and semver
.. Link
https://packaging.pypa.io/en/latest/_modules/packaging/version.html#InvalidVersion
When packaging for PyPI, your versions are defined through `PEP 440`_.
This is the standard version scheme for Python packages and
implemented by the :class:`packaging.version.Version` class.
However, these versions are different from semver versions
(cited from `PEP 440`_):
* The "Major.Minor.Patch" (described in this PEP as "major.minor.micro")
aspects of semantic versioning (clauses 1-8 in the 2.0.0
specification) are fully compatible with the version scheme defined
in this PEP, and abiding by these aspects is encouraged.
* Semantic versions containing a hyphen (pre-releases - clause 10)
or a plus sign (builds - clause 11) are *not* compatible with this PEP
and are not permitted in the public version field.
In other words, it's not always possible to convert between these different
versioning schemes without information loss. It depends on what parts are
used. The following table gives a mapping between these two versioning
schemes:
+--------------+----------------+
| PyPI Version | Semver version |
+==============+================+
| ``epoch`` | n/a |
+--------------+----------------+
| ``major`` | ``major`` |
+--------------+----------------+
| ``minor`` | ``minor`` |
+--------------+----------------+
| ``micro`` | ``patch`` |
+--------------+----------------+
| ``pre`` | ``prerelease`` |
+--------------+----------------+
| ``dev`` | ``build`` |
+--------------+----------------+
| ``post`` | n/a |
+--------------+----------------+
.. _convert_pypi_to_semver:
From PyPI to semver
-------------------
We distinguish between the following use cases:
* **"Incomplete" versions**
If you only have a major part, this shouldn't be a problem.
The initializer of :class:`semver.Version <semver.version.Version>` takes
care to fill missing parts with zeros (except for major).
.. code-block:: python
>>> from packaging.version import Version as PyPIVersion
>>> from semver import Version
>>> p = PyPIVersion("3.2")
>>> p.release
(3, 2)
>>> Version(*p.release)
Version(major=3, minor=2, patch=0, prerelease=None, build=None)
* **Major, minor, and patch**
This is the simplest and most compatible approch. Both versioning
schemes are compatible without information loss.
.. code-block:: python
>>> p = PyPIVersion("3.0.0")
>>> p.base_version
'3.0.0'
>>> p.release
(3, 0, 0)
>>> Version(*p.release)
Version(major=3, minor=0, patch=0, prerelease=None, build=None)
* **With** ``pre`` **part only**
A prerelease exists in both versioning schemes. As such, both are
a natural candidate. A prelease in PyPI version terms is the same
as a "release candidate", or "rc".
.. code-block:: python
>>> p = PyPIVersion("2.1.6.pre5")
>>> p.base_version
'2.1.6'
>>> p.pre
('rc', 5)
>>> pre = "".join([str(i) for i in p.pre])
>>> Version(*p.release, pre)
Version(major=2, minor=1, patch=6, prerelease='rc5', build=None)
* **With only development version**
Semver doesn't have a "development" version.
However, we could use Semver's ``build`` part:
.. code-block:: python
>>> p = PyPIVersion("3.0.0.dev2")
>>> p.base_version
'3.0.0'
>>> p.dev
2
>>> Version(*p.release, build=f"dev{p.dev}")
Version(major=3, minor=0, patch=0, prerelease=None, build='dev2')
* **With a** ``post`` **version**
Semver doesn't know the concept of a post version. As such, there
is currently no way to convert it reliably.
* **Any combination**
There is currently no way to convert a PyPI version which consists
of, for example, development *and* post parts.
You can use the following function to convert a PyPI version into
semver:
.. code-block:: python
def convert2semver(ver: packaging.version.Version) -> semver.Version:
"""Converts a PyPI version into a semver version
:param ver: the PyPI version
:return: a semver version
:raises ValueError: if epoch or post parts are used
"""
if not ver.epoch:
raise ValueError("Can't convert an epoch to semver")
if not ver.post:
raise ValueError("Can't convert a post part to semver")
pre = None if not ver.pre else "".join([str(i) for i in ver.pre])
return semver.Version(*ver.release, prerelease=pre, build=ver.dev)
.. _convert_semver_to_pypi:
From semver to PyPI
-------------------
We distinguish between the following use cases:
* **Major, minor, and patch**
.. code-block:: python
>>> from packaging.version import Version as PyPIVersion
>>> from semver import Version
>>> v = Version(1, 2, 3)
>>> PyPIVersion(str(v.finalize_version()))
<Version('1.2.3')>
* **With** ``pre`` **part only**
.. code-block:: python
>>> v = Version(2, 1, 4, prerelease="rc1")
>>> PyPIVersion(str(v))
<Version('2.1.4rc1')>
* **With only development version**
.. code-block:: python
>>> v = Version(3, 2, 8, build="dev4")
>>> PyPIVersion(f"{v.finalize_version()}{v.build}")
<Version('3.2.8.dev4')>
If you are unsure about the parts of the version, the following
function helps to convert the different parts:
.. code-block:: python
def convert2pypi(ver: semver.Version) -> packaging.version.Version:
"""Converts a semver version into a version from PyPI
A semver prerelease will be converted into a
prerelease of PyPI.
A semver build will be converted into a development
part of PyPI
:param semver.Version ver: the semver version
:return: a PyPI version
"""
v = ver.finalize_version()
prerelease = ver.prerelease if ver.prerelease else ""
build = ver.build if ver.build else ""
return PyPIVersion(f"{v}{prerelease}{build}")
.. _PEP 440: https://www.python.org/dev/peps/pep-0440/
================================================
FILE: docs/advanced/create-subclasses-from-version.rst
================================================
.. _sec_creating_subclasses_from_versioninfo:
Creating Subclasses from Version
================================
.. meta::
:description lang=en:
Creating subclasses from Version class
If you do not like creating functions to modify the behavior of semver
(as shown in section :ref:`sec_dealing_with_invalid_versions`), you can
also create a subclass of the :class:`Version <semver.version.Version>` class.
For example, if you want to output a "v" prefix before a version,
but the other behavior is the same, use the following code:
.. literalinclude:: semverwithvprefix.py
:language: python
:lines: 4-
The derived class :class:`SemVerWithVPrefix` can be used like
the original class. Additionally, you can pass "incomplete"
version strings like ``v2.3``:
.. code-block:: python
>>> v1 = SemVerWithVPrefix.parse("v1.2.3")
>>> assert str(v1) == "v1.2.3"
>>> print(v1)
v1.2.3
>>> v2 = SemVerWithVPrefix.parse("v2.3")
>>> v2 > v1
True
>>> bad = SemVerWithVPrefix.parse("1.2.4")
Traceback (most recent call last):
...
ValueError: '1.2.4': not a valid semantic version tag. Must start with 'v' or 'V'
================================================
FILE: docs/advanced/deal-with-invalid-versions.rst
================================================
.. _sec_dealing_with_invalid_versions:
Dealing with Invalid Versions
=============================
.. meta::
:description lang=en:
Dealing with invalid versions
As semver follows the semver specification, it cannot parse version
strings which are considered "invalid" by that specification. The semver
library cannot know all the possible variations so you need to help the
library a bit.
For example, if you have a version string ``v1.2`` would be an invalid
semver version.
However, "basic" version strings consisting of major, minor,
and patch part, can be easy to convert. The following function extract this
information and returns a tuple with two items:
.. literalinclude:: coerce.py
:language: python
The function returns a *tuple*, containing a :class:`Version <semver.version.Version>`
instance or None as the first element and the rest as the second element.
The second element (the rest) can be used to make further adjustments.
For example:
.. code-block:: python
>>> coerce("v1.2")
(Version(major=1, minor=2, patch=0, prerelease=None, build=None), '')
>>> coerce("v2.5.2-bla")
(Version(major=2, minor=5, patch=2, prerelease=None, build=None), '-bla')
================================================
FILE: docs/advanced/display-deprecation-warnings.rst
================================================
.. _sec_display_deprecation_warnings:
Displaying Deprecation Warnings
===============================
.. meta::
:description lang=en:
Displaying and filtering deprecation warnings
By default, deprecation warnings are `ignored in Python <https://docs.python.org/3/library/warnings.html#warning-categories>`_.
This also affects semver's own warnings.
It is recommended that you turn on deprecation warnings in your scripts. Use one of
the following methods:
* Use the option `-Wd <https://docs.python.org/3/using/cmdline.html#cmdoption-w>`_
to enable default warnings:
* Directly running the Python command::
$ python3 -Wd scriptname.py
* Add the option in the shebang line (something like ``#!/usr/bin/python3``)
after the command::
#!/usr/bin/python3 -Wd
* In your own scripts add a filter to ensure that *all* warnings are displayed:
.. code-block:: python
import warnings
warnings.simplefilter("default")
# Call your semver code
For further details, see the section
`Overriding the default filter <https://docs.python.org/3/library/warnings.html#overriding-the-default-filter>`_
of the Python documentation.
================================================
FILE: docs/advanced/index.rst
================================================
Advanced topics
===============
.. meta::
:description lang=en:
Advanced topics for Python semver
.. toctree::
:maxdepth: 1
deal-with-invalid-versions
create-subclasses-from-version
display-deprecation-warnings
combine-pydantic-and-semver
convert-pypi-to-semver
version-from-file
================================================
FILE: docs/advanced/semverwithvprefix.py
================================================
from semver import Version
class SemVerWithVPrefix(Version):
"""
A subclass of Version which allows a "v" prefix
"""
@classmethod
def parse(cls, version: str) -> "SemVerWithVPrefix":
"""
Parse version string to a Version instance.
:param version: version string with "v" or "V" prefix
:raises ValueError: when version does not start with "v" or "V"
:return: a new instance
"""
if not version[0] in ("v", "V"):
raise ValueError(
f"{version!r}: not a valid semantic version tag. "
"Must start with 'v' or 'V'"
)
return super().parse(version[1:], optional_minor_and_patch=True)
def __str__(self) -> str:
# Reconstruct the tag
return "v" + super().__str__()
================================================
FILE: docs/advanced/version-from-file.rst
================================================
.. _sec_reading_versions_from_file:
Reading Versions from File
==========================
.. meta::
:description lang=en:
Reading versions from file
In cases where a version is stored inside a file, one possible solution
is to use the following function:
.. code-block:: python
import os
from typing import Union
from semver.version import Version
def get_version(path: Union[str, os.PathLike]) -> semver.Version:
"""
Construct a Version object from a file
:param path: A text file only containing the semantic version
:return: A :class:`Version` object containing the semantic
version from the file.
"""
version = open(path,"r").read().strip()
return Version.parse(version)
================================================
FILE: docs/api.rst
================================================
.. _api:
API Reference
=============
.. meta::
:description lang=en:
API reference about Python semver
.. currentmodule:: semver
Metadata :mod:`semver.__about__`
--------------------------------
.. automodule:: semver.__about__
Deprecated Functions in :mod:`semver._deprecated`
-------------------------------------------------
.. automodule:: semver._deprecated
.. autofunction:: semver._deprecated.compare
.. autofunction:: semver._deprecated.bump_build
.. autofunction:: semver._deprecated.bump_major
.. autofunction:: semver._deprecated.bump_minor
.. autofunction:: semver._deprecated.bump_patch
.. autofunction:: semver._deprecated.bump_prerelease
.. autofunction:: semver._deprecated.deprecated
.. autofunction:: semver._deprecated.finalize_version
.. autofunction:: semver._deprecated.format_version
.. autofunction:: semver._deprecated.match
.. autofunction:: semver._deprecated.max_ver
.. autofunction:: semver._deprecated.min_ver
.. autofunction:: semver._deprecated.parse
.. autofunction:: semver._deprecated.parse_version_info
.. autofunction:: semver._deprecated.replace
CLI Parsing :mod:`semver.cli`
-----------------------------
.. automodule:: semver.cli
.. autofunction:: semver.cli.cmd_bump
.. autofunction:: semver.cli.cmd_check
.. autofunction:: semver.cli.cmd_compare
.. autofunction:: semver.cli.createparser
.. autofunction:: semver.cli.main
.. autofunction:: semver.cli.process
Entry point :mod:`semver.__main__`
----------------------------------
.. automodule:: semver.__main__
Version Handling :mod:`semver.version`
--------------------------------------
.. automodule:: semver.version
.. autoclass:: semver.version.VersionInfo
.. autoclass:: semver.version.Version
:members:
:special-members: __iter__, __eq__, __ne__, __lt__, __le__, __gt__, __ge__, __getitem__, __hash__, __repr__, __str__
================================================
FILE: docs/build-semver.rst
================================================
.. _build-semver:
Building semver
===============
.. meta::
:description lang=en:
Building semver
.. _Installing uv: https://docs.astral.sh/uv/getting-started/installation/
This project changed its way how it is built over time. We used to have
a :file:`setup.py` file, but switched to a :file:`pyproject.toml` setup.
The build process is managed by :command:`uv` command.
You need:
* Python 3.7 or newer.
* The :mod:`setuptools` module version 61 or newer which is used as
a build backend.
* The command :command:`uv` from Astral. Refer to the section
`Installing uv`_ for more information.
To build semver, run::
uv build
After the command is finished, you can find two files in the :file:`dist` folder: a ``.tar.gz`` and a ``.whl`` file.
================================================
FILE: docs/changelog-semver3-devel.rst
================================================
#############################
Changelog semver3 development
#############################
This site contains all the changes during the development phase.
.. _semver-3.0.0-dev.4:
Version 3.0.0-dev.4
===================
:Released: 2022-12-18
:Maintainer:
.. _semver-3.0.0-dev.4-bugfixes:
Bug Fixes
---------
* :gh:`374`: Correct Towncrier's config entries in the :file:`pyproject.toml` file.
The old entries ``[[tool.towncrier.type]]`` are deprecated and need
to be replaced by ``[tool.towncrier.fragment.<TYPE>]``.
.. _semver-3.0.0-dev.4-deprecations:
Deprecations
------------
* :gh:`372`: Deprecate support for Python 3.6.
Python 3.6 reached its end of life and isn't supported anymore.
At the time of writing (Dec 2022), the lowest version is 3.7.
Although the `poll <https://github.com/python-semver/python-semver/discussions/371>`_
didn't cast many votes, the majority agree to remove support for
Python 3.6.
.. _semver-3.0.0-dev.4-doc:
Improved Documentation
----------------------
* :gh:`335`: Add new section "Converting versions between PyPI and semver" the limitations
and possible use cases to convert from one into the other versioning scheme.
* :gh:`340`: Describe how to get version from a file
* :gh:`343`: Describe combining Pydantic with semver in the "Advanced topic"
section.
* :gh:`350`: Restructure usage section. Create subdirectory "usage/" and splitted
all section into different files.
* :gh:`351`: Introduce new topics for:
* "Migration to semver3"
* "Advanced topics"
.. _semver-3.0.0-dev.4-features:
Features
--------
* :pr:`359`: Add optional parameter ``optional_minor_and_patch`` in :meth:`.Version.parse` to allow optional
minor and patch parts.
* :pr:`362`: Make :meth:`.Version.match` accept a bare version string as match expression, defaulting to
equality testing.
* :gh:`364`: Enhance :file:`pyproject.toml` to make it possible to use the
:command:`pyproject-build` command from the build module.
For more information, see :ref:`build-semver`.
* :gh:`365`: Improve :file:`pyproject.toml`.
* Use setuptools, add metadata. Taken approach from
`A Practical Guide to Setuptools and Pyproject.toml
<https://godatadriven.com/blog/a-practical-guide-to-setuptools-and-pyproject-toml/>`_.
* Doc: Describe building of semver
* Remove :file:`.travis.yml` in :file:`MANIFEST.in`
(not needed anymore)
* Distinguish between Python 3.6 and others in :file:`tox.ini`
* Add skip_missing_interpreters option for :file:`tox.ini`
* GH Action: Upgrade setuptools and setuptools-scm and test
against 3.11.0-rc.2
.. _semver-3.0.0-dev.4-internal:
Trivial/Internal Changes
------------------------
* :gh:`378`: Fix some typos in Towncrier configuration
----
.. _semver-3.0.0-dev.3:
Version 3.0.0-dev.3
===================
:Released: 2022-01-19
:Maintainer: Tom Schraitle
.. _semver-3.0.0-dev.3-bugfixes:
Bug Fixes
---------
* :gh:`310`: Rework API documentation.
Follow a more "semi-manual" attempt and add auto directives
into :file:`docs/api.rst`.
.. _semver-3.0.0-dev.3-docs:
Improved Documentation
----------------------
* :gh:`312`: Rework "Usage" section.
* Mention the rename of :class:`~semver.version.VersionInfo` to
:class:`~semver.version.Version` class
* Remove semver. prefix in doctests to make examples shorter
* Correct some references to dunder methods like
:func:`~semver.version.Version.__getitem__`,
:func:`~semver.version.Version.__gt__` etc.
* Remove inconsistencies and mention module level function as
deprecated and discouraged from using
* Make empty :py:class:`python:super` call in :file:`semverwithvprefix.py` example
* :gh:`315`: Improve release procedure text
.. _semver-3.0.0-dev.3-trivial:
Trivial/Internal Changes
------------------------
* :gh:`309`: Some (private) functions from the :mod:`semver.version`
module has been changed.
The following functions got renamed:
* function :func:`semver.version.comparator` got renamed to
:func:`semver.version._comparator` as it is only useful
inside the :class:`~semver.version.Version` class.
* function :func:`semver.version.cmp` got renamed to
:func:`semver.version._cmp` as it is only useful
inside the :class:`~semver.version.Version` class.
The following functions got integrated into the
:class:`~semver.version.Version` class:
* function :func:`semver.version._nat_cmd` as a classmethod
* function :func:`semver.version.ensure_str`
* :gh:`313`: Correct :file:`tox.ini` for ``changelog`` entry to skip
installation for semver. This should speed up the execution
of towncrier.
* :gh:`316`: Comparisons of :class:`~semver.version.Version` class and other
types return now a :py:data:`python:NotImplemented` constant instead
of a :py:exc:`python:TypeError` exception.
In the Python documentation, :py:data:`python:NotImplemented` recommends
returning this constant when comparing with :py:meth:`__gt__ <python:object.__gt__>`, :py:meth:`__lt__ <python:object.__lt__>`,
and other comparison operators "to indicate that the operation is
not implemented with respect to the other type".
* :gh:`319`: Introduce stages in :file:`.travis.yml`
The config file contains now two stages: check and test. If
check fails, the test stage won't be executed. This could
speed up things when some checks fails.
* :gh:`322`: Switch from Travis CI to GitHub Actions.
* :gh:`347`: Support Python 3.10 in GitHub Action and other config files.
----
.. _semver-3.0.0-dev.2:
Version 3.0.0-dev.2
===================
:Released: 2020-11-01
:Maintainer: Tom Schraitle
.. _semver-3.0.0-dev.2-deprecations:
Deprecations
------------
* :gh:`169`: Deprecate CLI functions not imported from :mod:`semver.cli`.
.. _semver-3.0.0-dev.2-features:
Features
--------
* :gh:`169`: Create semver package and split code among different modules in the packages.
* Remove :file:`semver.py`
* Create :file:`src/semver/__init__.py`
* Create :file:`src/semver/cli.py` for all CLI methods
* Create :file:`src/semver/_deprecated.py` for the ``deprecated`` decorator and other deprecated functions
* Create :file:`src/semver/__main__.py` to allow calling the CLI using :command:`python -m semver`
* Create :file:`src/semver/_types.py` to hold type aliases
* Create :file:`src/semver/version.py` to hold the :class:`~semver.version.Version` class (old name :class:`~semver.version.VersionInfo`) and its utility functions
* Create :file:`src/semver/__about__.py` for all the metadata variables
* :gh:`305`: Rename :class:`~semver.version.VersionInfo` to :class:`~semver.version.Version` but keep an alias for compatibility
.. _semver-3.0.0-dev.2-docs:
Improved Documentation
----------------------
* :gh:`304`: Several improvements in documentation:
* Reorganize API documentation.
* Add migration chapter from semver2 to semver3.
* Distinguish between changlog for version 2 and 3
* :gh:`305`: Add note about :class:`~semver.version.Version` rename.
.. _semver-3.0.0-dev.2-trivial:
Trivial/Internal Changes
------------------------
* :gh:`169`: Adapted infrastructure code to the new project layout.
* Replace :file:`setup.py` with :file:`setup.cfg` because the :file:`setup.cfg` is easier to use
* Adapt documentation code snippets where needed
* Adapt tests
* Changed the ``deprecated`` to hardcode the ``semver`` package name in the warning.
Increase coverage to 100% for all non-deprecated APIs
* :gh:`304`: Support PEP-561 :file:`py.typed`.
According to the mentioned PEP:
"Package maintainers who wish to support type checking
of their code MUST add a marker file named :file:`py.typed`
to their package supporting typing."
Add package_data to :file:`setup.cfg` to include this marker in dist
and whl file.
----
.. _semver-3.0.0-dev.1:
Version 3.0.0-dev.1
===================
:Released: 2020-10-26
:Maintainer: Tom Schraitle
.. _semver-3.0.0-dev.1-deprecations:
Deprecations
------------
* :pr:`290`: For semver 3.0.0-alpha0:
* Remove anything related to Python2
* In :file:`tox.ini` and :file:`.travis.yml`
Remove targets py27, py34, py35, and pypy.
Add py38, py39, and nightly (allow to fail)
* In :file:`setup.py` simplified file and remove
``Tox`` and ``Clean`` classes
* Remove old Python versions (2.7, 3.4, 3.5, and pypy)
from Travis
* :gh:`234`: In :file:`setup.py` simplified file and remove
``Tox`` and ``Clean`` classes
.. _semver-3.0.0-dev.1-features:
Features
--------
* :pr:`290`: Create semver 3.0.0-alpha0
* Update :file:`README.rst`, mention maintenance
branch ``maint/v2``.
* Remove old code mainly used for Python2 compatibility,
adjusted code to support Python3 features.
* Split test suite into separate files under :file:`tests/`
directory
* Adjust and update :file:`setup.py`. Requires Python >=3.6.*
Extract metadata directly from source (affects all the :data:`~semver.__about__.__version__`,
:data:`~semver.__about__.__author__` etc. variables)
* :gh:`270`: Configure Towncrier (:pr:`273`:)
* Add :file:`changelog.d/.gitignore` to keep this directory
* Create :file:`changelog.d/README.rst` with some descriptions
* Add :file:`changelog.d/_template.rst` as Towncrier template
* Add ``[tool.towncrier]`` section in :file:`pyproject.toml`
* Add "changelog" target into :file:`tox.ini`. Use it like
:command:`tox -e changelog -- CMD` whereas ``CMD`` is a
Towncrier command. The default :command:`tox -e changelog`
calls Towncrier to create a draft of the changelog file
and output it to stdout.
* Update documentation and add include a new section
"Changelog" included from :file:`changelog.d/README.rst`.
* :gh:`276`: Document how to create a sublass from :class:`~semver.version.VersionInfo` class
* :gh:`213`: Add typing information
.. _semver-3.0.0-dev.1-bugfixes:
Bug Fixes
---------
* :gh:`291`: Disallow negative numbers in VersionInfo arguments
for ``major``, ``minor``, and ``patch``.
.. _semver-3.0.0-dev.1-docs:
Improved Documentation
----------------------
* :pr:`290`: Several improvements in the documentation:
* New layout to distinguish from the semver2 development line.
* Create new logo.
* Remove any occurances of Python2.
* Describe changelog process with Towncrier.
* Update the release process.
.. _semver-3.0.0-dev.1-trivial:
Trivial/Internal Changes
------------------------
* :pr:`290`: Add supported Python versions to :command:`black`.
================================================
FILE: docs/changelog.rst
================================================
.. _change-log:
.. include:: ../CHANGELOG.rst
================================================
FILE: docs/conf.py
================================================
#!/usr/bin/env python3
#
# python-semver documentation build configuration file
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import re
from datetime import date
from pathlib import Path
SRC_DIR = Path(__file__).absolute().parent.parent / "src"
YEAR = date.today().year
def find_version(path: Path) -> str:
"""
Build a path from *file_paths* and search for a ``__version__``
string inside.
"""
content = Path(path).read_text(encoding="utf-8")
version_match = re.search(
r"^__version__ = ['\"]([^'\"]*)['\"]",
content,
re.MULTILINE
)
if version_match:
return version_match.group(1)
raise RuntimeError("Unable to find version string.")
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"sphinx.ext.autodoc",
"sphinx_autodoc_typehints",
"sphinx.ext.intersphinx",
"sphinx.ext.extlinks",
]
# Autodoc configuration
autoclass_content = "class"
autodoc_typehints = "signature"
autodoc_member_order = "alphabetical"
add_function_parentheses = True
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
source_suffix = ".rst"
# The master toctree document.
master_doc = "index"
# General information about the project.
project = "python-semver"
copyright = f"{YEAR}, Kostiantyn Rybnikov and all"
author = "Kostiantyn Rybnikov, Tom Schraitle, Sebastien Celles, and others"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
release = find_version(SRC_DIR / "semver/__about__.py")
# The full version, including alpha/beta/rc tags.
version = release # .rsplit(u".", 1)[0]
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = "en"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# Markup to shorten external links
# See https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html
extlinks = {
"gh": ("https://github.com/python-semver/python-semver/issues/%s", "#%s"),
"pr": ("https://github.com/python-semver/python-semver/pull/%s", "PR #%s"),
}
# Link to other projects’ documentation
# See https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html
intersphinx_mapping = {
# Download it from the root with:
# wget -O docs/inventories/python-objects.inv https://docs.python.org/3/objects.inv
"python": ("https://docs.python.org/3", (None, "inventories/python-objects.inv")),
# wget -O docs/inventories/pydantic.inv https://docs.pydantic.dev/latest/objects.inv
"pydantic": (
"https://docs.pydantic.dev/latest/",
(None, "inventories/pydantic.inv"),
),
}
# Avoid side-effects (namely that documentations local references can
# suddenly resolve to an external location.)
intersphinx_disabled_reftypes = ["*"]
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = "alabaster"
templates_path = ["_templates"]
GITHUB_URL = "https://github.com/python-semver/python-semver"
html_theme_options = {
# -- Basics
#: Text blurb about your project to appear under the logo:
# "description": "Semantic versioning",
#: Makes the sidebar "fixed" or pinned in place:
"fixed_sidebar": True,
#: Relative path to $PROJECT/_static to logo image:
"logo": "logo.svg",
#: Set to true to insert your site's project name under
#: the logo:
# "logo_name": True,
#: CSS width specifier controller default sidebar width:
"sidebar_width": "25%",
#: CSS width specifier controlling default content/page width:
"page_width": "auto",
#: CSS width specifier controlling default body text width:
"body_max_width": "auto",
#
# -- Service Links and Badges
#: Contains project name and user of GitHub:
"github_user": "python-semver",
"github_repo": "python-semver",
#: whether to link to your GitHub:
"github_button": True,
#:
"github_type": "star",
#: whether to apply a "Fork me on Github" banner
#: in the top right corner of the page:
# "github_banner": True,
#
# -- Non-service sidebar control
#: Dictionary mapping link names to link targets:
"extra_nav_links": {
"PyPI": "https://pypi.org/project/semver/",
"Libraries.io": "https://libraries.io/pypi/semver",
},
#: Boolean determining whether all TOC entries that
#: are not ancestors of the current page are collapsed:
"sidebar_collapse": True,
#
# -- Header/footer options
#: used to display next and previous links above and
#: below the main page content
"show_relbars": True,
"show_relbar_top": True,
#
# -- Style colors
# "anchor": "",
# "anchor_hover_bg": "",
# "anchor_hover_fg": "",
"narrow_sidebar_fg": "lightgray",
#
# -- Fonts
# "code_font_size": "",
"font_family": "'Roboto',sans-serif",
"head_font_family": "'Roboto Slab',serif",
"code_font_family": "'Roboto Mono',monospace",
"font_size": "1.20rem",
}
html_static_path = ["_static"]
html_css_files = ["css/custom.css"]
html_sidebars = {
"**": [
"about.html", # theme_logo
# 'relations.html', # prev and next
"searchbox.html", # basic/searchbox.html
"navigation.html", # TOC
# 'donate.html',
]
}
# Canonical link relation
if os.environ.get("READTHEDOCS_CANONICAL_URL"):
html_baseurl = f"{os.environ['READTHEDOCS_CANONICAL_URL']}"
# html_logo = "logo.svg"
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = "semverdoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(
master_doc,
"semver.tex",
"python-semver Documentation",
"Kostiantyn Rybnikov and all",
"manual",
)
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
manpage_doc = "pysemver"
man_pages = [
(
manpage_doc,
"pysemver",
"Helper script for Semantic Versioning",
["Thomas Schraitle"],
1,
)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(
master_doc,
"semver",
"python-semver Documentation",
author,
"semver",
"One line description of project.",
"Miscellaneous",
)
]
================================================
FILE: docs/contribute/add-changelog-entry.rst
================================================
.. _add-changelog:
Adding a Changelog Entry
========================
.. meta::
:description lang=en:
Adding a changelog entry with Towncrier
.. include:: ../../changelog.d/README.rst
:start-after: -text-begin-
================================================
FILE: docs/contribute/doc-semver.rst
================================================
.. _doc:
Documenting semver
==================
.. meta::
:description lang=en:
Documenting semver with type annotations, docstrings, Sphinx directives
Documenting the features of semver is very important. It gives our developers
an overview what is possible with semver, how it "feels", and how it is
used efficiently.
.. note::
To build the documentation locally use the following command::
$ tox -e docs
The built documentation is available in :file:`docs/_build/html`.
A new feature is *not* complete if it isn't proberly documented. A good
documentation includes:
* **Type annotations**
This library supports type annotations. Therefore, each function
or method requires types for each arguments and return objects.
Exception of this rule is ``self``.
* **A docstring**
Each docstring contains a summary line, a linebreak, an optional
directive (see next item), the description of its arguments in
`Sphinx style`_, and an optional doctest.
The docstring is extracted and reused in the :ref:`api` section.
An appropriate docstring looks like this::
def to_tuple(self) -> VersionTuple:
"""
Convert the Version object to a tuple.
.. versionadded:: 2.10.0
Renamed ``VersionInfo._astuple`` to ``VersionInfo.to_tuple`` to
make this function available in the public API.
:return: a tuple with all the parts
>>> semver.Version(5, 3, 1).to_tuple()
(5, 3, 1, None, None)
"""
* **An optional directive**
If you introduce a new feature, change a function/method, or remove something,
it is a good practice to introduce Sphinx directives into the docstring.
This gives the reader an idea what version is affected by this change.
The first required argument, ``VERSION``, defines the version when this change
was introduced. You can choose from:
* ``.. versionadded:: VERSION``
Use this directive to describe a new feature.
* ``.. versionchanged:: VERSION``
Use this directive to describe when something has changed, for example,
new parameters were added, changed side effects, different return values, etc.
* ``.. deprecated:: VERSION``
Use this directive when a feature is deprecated. Describe what should
be used instead, if appropriate.
Add such a directive *after* the summary line, as shown above.
* **The documentation**
A docstring is good, but in most cases it is too short. API documentation
cannot replace good user documentation.
Describe *how* to use your new feature in the documentation.
Here you can give your readers more examples, describe it in a broader
context, or show edge cases.
.. _Sphinx style: https://sphinx-rtd-tutorial.rtfd.io/en/latest/docstrings.html
================================================
FILE: docs/contribute/finish-release.rst
================================================
.. _finish-release:
Finish the Release
==================
.. meta::
:description lang=en:
Finish the semver release by creating tags
1. Create a tag:
$ git tag -a x.x.x
It’s recommended to use the generated Tox output from the Changelog.
2. Push the tag:
$ git push –tags
3. In `GitHub Release
page <https://github.com/python-semver/python-semver/release>`_
document the new release. Select the tag from the last step and copy
the content of the tag description into the release description.
4. Announce it in
https://github.com/python-semver/python-semver/discussions/categories/announcements.
You’re done! Celebrate!
================================================
FILE: docs/contribute/index.rst
================================================
.. _contributing:
Contributing to semver
======================
.. meta::
:description lang=en:
Contributing to Python semver
The semver source code is managed using Git and is hosted on GitHub::
git clone git://github.com/python-semver/python-semver
.. include:: prerequisites.rst
:start-after: -text-begin-
.. toctree::
:maxdepth: 1
:caption: More topics
:includehidden:
report-bugs
run-test-suite
doc-semver
add-changelog-entry
release-procedure
finish-release
.. _pull request: https://github.com/python-semver/python-semver/pulls
================================================
FILE: docs/contribute/prerequisites.rst
================================================
Prerequisites
-------------
.. meta::
:description lang=en:
Overview of prerequisites for contributing
.. -text-begin-
Before you make changes to the code, we would highly appreciate if you
consider the following general requirements:
* Make sure your code adheres to the `Semantic Versioning`_ specification.
* Check if your feature is covered by the Semantic Versioning specification.
If not, ask on its GitHub project https://github.com/semver/semver.
.. _Semantic Versioning: https://semver.org
================================================
FILE: docs/contribute/release-procedure.rst
================================================
Release Procedure
=================
.. meta::
:description lang=en:
Release procedure: prepare and create the release
The following procedures gives a short overview of what steps are needed
to create a new release.
These steps are interesting for the release manager only.
Prepare the Release
-------------------
1. Verify that:
- all issues for a new release are closed:
https://github.com/python-semver/python-semver/issues.
- all pull requests that should be included in this release are
merged: https://github.com/python-semver/python-semver/pulls.
- continuous integration for latest build was passing:
https://github.com/python-semver/python-semver/actions.
2. Create a new branch ``release/<VERSION>``.
3. If one or several supported Python versions have been removed or
added, verify that the following files have been updated:
- :file:`setup.cfg`
- :file:`tox.ini`
- :file:`.git/workflows/pythonpackage.yml`
- :file:`.github/workflows/python-testing.yml`
4. Verify that the version in file :file:`src/semver/__about__.py`
has been updated and follows the `Semver <https://semver.org>`_
specification.
5. Add eventually new contributor(s) to
`CONTRIBUTORS <https://github.com/python-semver/python-semver/blob/master/CONTRIBUTORS>`_.
6. Check if all changelog entries are created. If some are missing,
`create
them <https://python-semver.readthedocs.io/en/latest/development.html#adding-a-changelog-entry>`__.
7. Show the new draft
`CHANGELOG <https://github.com/python-semver/python-semver/blob/master/CHANGELOG.rst>`_ entry for the latest release with:
::
$ tox -e changelog
Check the output. If you are not happy, update the files in the
``changelog.d/`` directory. If everything is okay, build the new
``CHANGELOG`` with:
::
$ tox -e changelog -- build
8. Build the documentation and check the output:
::
$ tox -e docs
9. Commit all changes, push, and create a pull request.
Create the New Release
----------------------
1. Ensure that long description
(`README.rst <https://github.com/python-semver/python-semver/blob/master/README.rst>`_)
can be correctly rendered by Pypi using
``restview --long-description``
2. Clean up your local Git repository. Be careful, as it **will remove
all files** which are not versioned by Git:
::
$ git clean -xfd
Before you create your distribution files, clean the directory too:
::
$ rm dist/*
3. Create the distribution files (wheel and source):
::
$ tox -e prepare-dist
4. Upload the wheel and source to TestPyPI first:
.. code:: bash
$ twine upload --repository-url https://test.pypi.org/legacy/ dist/*
If you have a ``~/.pypirc`` with a ``testpypi`` section, the upload
can be simplified:
::
$ twine upload --repository testpypi dist/*
5. Check if everything is okay with the wheel. Check also the web site
``https://test.pypi.org/project/<VERSION>/``
6. If everything looks fine, merge the pull request.
7. Upload to PyPI:
.. code:: bash
$ git clean -xfd
$ tox -e prepare-dist
$ twine upload dist/*
8. Go to https://pypi.org/project/semver/ to verify that new version is
online and the page is rendered correctly.
================================================
FILE: docs/contribute/report-bugs.rst
================================================
.. _report-bugs:
Reporting Bugs and Asking Questions
-----------------------------------
.. meta::
:description lang=en:
Reporting bugs and asking questions about semver
If you think you have encountered a bug in semver or have an idea for a new
feature? Great! We like to hear from you!
There are several options to participate:
* Open a new topic on our `GitHub discussion <gh_discussions>`_ page.
Tell us our ideas or ask your questions.
* Look into our GitHub `issues`_ tracker or open a new issue.
.. _issues: https://github.com/python-semver/python-semver/issues
.. _gh_discussions: https://github.com/python-semver/python-semver/discussions
================================================
FILE: docs/contribute/run-test-suite.rst
================================================
.. _testsuite:
Running the Test Suite
======================
.. meta::
:description lang=en:
Running the test suite through tox
We use `pytest`_ and `tox`_ to run tests against all supported Python
versions. All test dependencies are resolved automatically.
You can decide to run the complete test suite or only part of it:
* To run all tests, use::
$ tox
If you have not all Python interpreters installed on your system
it will probably give you some errors (``InterpreterNotFound``).
To avoid such errors, use::
$ tox --skip-missing-interpreters
It is possible to use one or more specific Python versions. Use the ``-e``
option and one or more abbreviations (``py37`` for Python 3.7,
``py38`` for Python 3.8 etc.)::
$ tox -e py37
$ tox -e py37,py38
To get a complete list and a short description, run::
$ tox -av
* To run only a specific test, pytest requires the syntax
``TEST_FILE::TEST_FUNCTION``.
For example, the following line tests only the function
:func:`test_immutable_major` in the file :file:`test_bump.py` for all
Python versions::
$ tox -e py37 -- tests/test_bump.py::test_should_bump_major
By default, pytest prints only a dot for each test function. To
reveal the executed test function, use the following syntax::
$ tox -- -v
You can combine the specific test function with the ``-e`` option, for
example, to limit the tests for Python 3.7 and 3.8 only::
$ tox -e py37,py38 -- tests/test_bump.py::test_should_bump_major
Our code is checked against formatting, style, type, and docstring issues
(`black`_, `flake8`_, `mypy`_, and `docformatter`_).
It is recommended to run your tests in combination with :command:`checks`,
for example::
$ tox -e checks,py37,py38
.. _black: https://black.rtfd.io
.. _docformatter: https://pypi.org/project/docformatter/
.. _flake8: https://flake8.rtfd.io
.. _mypy: http://mypy-lang.org/
.. _pytest: http://pytest.org/
.. _tox: https://tox.rtfd.org/
================================================
FILE: docs/index.rst
================================================
Semver |version| -- Semantic Versioning
=======================================
.. meta::
:description lang=en:
Semantic versioning for Python
.. include:: readme.rst
.. toctree::
:maxdepth: 2
:caption: Contents
:hidden:
:numbered:
build-semver
install
usage/index
migration/index
advanced/index
contribute/index
version-policy
api
.. toctree::
:maxdepth: 2
:caption: CLI
:hidden:
pysemver
.. toctree::
:maxdepth: 1
:caption: Development
:hidden:
changelog
changelog-semver3-devel
Indices and Tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
================================================
FILE: docs/install.rst
================================================
Installing semver
=================
.. meta::
:description lang=en:
Installing semver on the system
Release Policy
--------------
As semver uses `Semantic Versioning`_, breaking changes are only introduced in major
releases (incremented ``X`` in "X.Y.Z").
Refer to section :ref:`version-policy` for a general overview.
For users who want or need to stay with major 3 releases only, add the
following version restriction (:file:`setup.py`, :file:`requirements.txt`,
or :file:`pyproject.toml`)::
semver>=3,<4
This line avoids surprises. You will get any updates within the major 3 release like 3.1.x and above. However, you will never get an update for semver 4.0.0.
For users who have to stay with major 2 releases only, use the following line::
semver>=2,<3
With Pip
--------
.. code-block:: bash
:name: install-pip
pip3 install semver
If you want to install this specific version (for example, 3.0.0), use the command :command:`pip`
with an URL and its version:
.. parsed-literal::
pip3 install git+https://github.com/python-semver/python-semver.git@3.0.0
With uv
-------
First, install the :command:`uv` command. Refer to https://docs.astral.sh/uv/getting-started/installation/ for more information.
Then use the command :command:`uv` to install the package:
.. code-block:: bash
:name: install-uv
uv pip install semver
Linux Distributions
-------------------
.. note::
Some Linux distributions can have outdated packages.
These outdated packages does not contain the latest bug fixes or new features.
If you need a newer package, you have these option:
* Ask the maintainer to update the package.
* Update the package for your favorite distribution and submit it.
* Use a Python virtual environment and :command:`pip install`.
Arch Linux
^^^^^^^^^^
1. Enable the community repositories first:
.. code-block:: ini
[community]
Include = /etc/pacman.d/mirrorlist
2. Install the package::
$ pacman -Sy python-semver
Debian
^^^^^^
1. Update the package index::
$ sudo apt-get update
2. Install the package::
$ sudo apt-get install python3-semver
Fedora
^^^^^^
.. code-block:: bash
$ dnf install python3-semver
FreeBSD
^^^^^^^
.. code-block:: bash
$ pkg install py36-semver
openSUSE
^^^^^^^^
1. Enable the ``devel:languages:python`` repository of the Open Build Service::
$ sudo zypper addrepo --refresh \
--name devel_languages_python \
"https://download.opensuse.org/repositories/devel:/languages:/python/\$releasever"
2. Install the package::
$ sudo zypper install --repo devel_languages_python python3-semver
Ubuntu
^^^^^^
1. Update the package index::
$ sudo apt-get update
2. Install the package::
$ sudo apt-get install python3-semver
.. _semantic versioning: https://semver.org/
================================================
FILE: docs/make.bat
================================================
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=python -msphinx
)
set SOURCEDIR=.
set BUILDDIR=_build
set SPHINXPROJ=semver
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The Sphinx module was not found. Make sure you have Sphinx installed,
echo.then set the SPHINXBUILD environment variable to point to the full
echo.path of the 'sphinx-build' executable. Alternatively you may add the
echo.Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd
================================================
FILE: docs/migration/index.rst
================================================
Migrating to semver3
====================
.. meta::
:description lang=en:
Migrating from semver version 2 to version 3
.. toctree::
:maxdepth: 1
migratetosemver3
replace-deprecated-functions.rst
================================================
FILE: docs/migration/migratetosemver3.rst
================================================
.. _semver2-to-3:
Migrating from semver2 to semver3
=================================
.. meta::
:description lang=en:
Migrating from semver2 to semver3
This section describes the visible differences for
users and how your code stays compatible for semver3.
Some changes are backward incompatible.
Although the development team tries to make the transition
to semver3 as smooth as possible, at some point change
is inevitable.
For a more detailed overview of all the changes, refer
to our :ref:`change-log`.
Use Version instead of VersionInfo
----------------------------------
The :class:`~semver.version.VersionInfo` has been renamed to
:class:`~semver.version.Version` to have a more succinct name.
An alias has been created to preserve compatibility but
using the old name has been deprecated and will be removed
in future versions.
If you still need the old version, use this line:
.. code-block:: python
from semver.version import Version as VersionInfo
Use semver.cli instead of semver
--------------------------------
All functions related to CLI parsing are moved to :mod:`semver.cli`.
If you need such functions, like :meth:`~semver.cli.cmd_bump`,
import it from :mod:`semver.cli` in the future:
.. code-block:: python
from semver.cli import cmd_bump
Use semver.Version.is_valid instead of semver.Version.isvalid
-------------------------------------------------------------
The pull request :pr:`284` introduced the method :meth:`~semver.version.Version.is_compatible`. To keep consistency, the development team
decided to rename the :meth:`~semver.Version.isvalid` to :meth:`~semver.Version.is_valid`.
================================================
FILE: docs/migration/replace-deprecated-functions.rst
================================================
.. _sec_replace_deprecated_functions:
Replacing Deprecated Functions
==============================
.. meta::
:description lang=en:
Replacing deprecated functions
.. versionchanged:: 2.10.0
The development team of semver has decided to deprecate certain functions on
the module level. The preferred way of using semver is through the
:class:`~semver.version.Version` class.
The deprecated functions can still be used in version 2.10.0 and above.
However, in future versions of semver, the deprecated functions will be removed.
Deprecated Module Level Functions
---------------------------------
The following list shows the deprecated module level functions and how you can replace
them with code which is compatible for future versions:
* :func:`semver.bump_major <semver._deprecated.bump_major>`,
:func:`semver.bump_minor <semver._deprecated.bump_minor>`,
:func:`semver.bump_patch <semver._deprecated.bump_patch>`,
:func:`semver.bump_prerelease <semver._deprecated.bump_prerelease>`,
:func:`semver.bump_build <semver._deprecated.bump_build>`
Replace them with the respective methods of the :class:`~semver.version.Version`
class.
For example, the function :func:`semver.bump_major <semver._deprecated.bump_major>` is replaced by
:meth:`Version.bump_major <semver.version.Version.bump_major>` and calling the ``str(versionobject)``:
.. code-block:: python
>>> s1 = semver.bump_major("3.4.5")
>>> s2 = str(Version.parse("3.4.5").bump_major())
>>> s1 == s2
True
Likewise with the other module level functions.
* :func:`semver.finalize_version <semver._deprecated.finalize_version>`
Replace it with :meth:`Version.finalize_version <semver.version.Version.finalize_version>`:
.. code-block:: python
>>> s1 = semver.finalize_version('1.2.3-rc.5')
>>> s2 = str(semver.Version.parse('1.2.3-rc.5').finalize_version())
>>> s1 == s2
True
* :func:`semver.format_version <semver._deprecated.format_version>`
Replace it with ``str(versionobject)``:
.. code-block:: python
>>> s1 = semver.format_version(5, 4, 3, 'pre.2', 'build.1')
>>> s2 = str(Version(5, 4, 3, 'pre.2', 'build.1'))
>>> s1 == s2
True
* :func:`semver.max_ver <semver._deprecated.max_ver>`
Replace it with ``max(version1, version2, ...)`` or ``max([version1, version2, ...])`` and a ``key``:
.. code-block:: python
>>> s1 = semver.max_ver("1.2.3", "1.2.4")
>>> s2 = max("1.2.3", "1.2.4", key=Version.parse)
>>> s1 == s2
True
* :func:`semver.min_ver <semver._deprecated.min_ver>`
Replace it with ``min(version1, version2, ...)`` or ``min([version1, version2, ...])`` and a ``key``:
.. code-block:: python
>>> s1 = semver.min_ver("1.2.3", "1.2.4")
>>> s2 = min("1.2.3", "1.2.4", key=Version.parse)
>>> s1 == s2
True
* :func:`semver.parse <semver._deprecated.parse>`
Replace it with :meth:`Version.parse <semver.version.Version.parse>` and call
:meth:`Version.to_dict <semver.version.Version.to_dict>`:
.. code-block:: python
>>> v1 = semver.parse("1.2.3")
>>> v2 = Version.parse("1.2.3").to_dict()
>>> v1 == v2
True
* :func:`semver.parse_version_info <semver._deprecated.parse_version_info>`
Replace it with :meth:`Version.parse <semver.version.Version.parse>`:
.. code-block:: python
>>> v1 = semver.parse_version_info("3.4.5")
>>> v2 = Version.parse("3.4.5")
>>> v1 == v2
True
* :func:`semver.replace <semver._deprecated.replace>`
Replace it with :meth:`Version.replace <semver.version.Version.replace>`:
.. code-block:: python
>>> s1 = semver.replace("1.2.3", major=2, patch=10)
>>> s2 = str(Version.parse('1.2.3').replace(major=2, patch=10))
>>> s1 == s2
True
Deprected Version methods
-------------------------
The following list shows the deprecated methods of the :class:`~semver.version.Version` class.
* :meth:`Version.isvalid <semver.version.Version.isvalid>`
Replace it with :meth:`Version.is_valid <semver.version.Version.is_valid>`:
Deprecated Classes
------------------
* :class:`VersionInfo <semver.version.VersionInfo>`
The class was renamed to :class:`~semver.version.Version`.
Don't use the old name anymore.
================================================
FILE: docs/pysemver.rst
================================================
:orphan:
pysemver |version|
==================
.. meta::
:description lang=en:
Commandline tool pysemver describing all commands and options
Synopsis
--------
.. _invocation:
.. code:: bash
pysemver <COMMAND> <OPTION>...
Description
-----------
The semver library provides a command line interface with the name
:command:`pysemver` to make the functionality accessible for shell
scripts. The script supports several subcommands.
Global Options
~~~~~~~~~~~~~~
.. program:: pysemver
.. option:: -h, --help
Display usage summary.
.. option:: --version
Show program's version number and exit.
Commands
--------
.. HINT: Sort the subcommands alphabetically
pysemver bump
~~~~~~~~~~~~~
Bump a version.
.. code:: bash
pysemver bump <PART> <VERSION>
.. option:: <PART>
The part to bump. Valid strings are ``major``, ``minor``,
``patch``, ``prerelease``, or ``build``. The part has the
following effects:
* ``major``: Raise the major part of the version and set
minor and patch to zero, remove prerelease and build.
* ``minor``: Raise the minor part of the version and set
patch to zero, remove prerelease and build.
* ``patch``: Raise the patch part of the version and
remove prerelease and build.
* ``prerelease`` Raise the prerelease of the version and
remove the build part.
* ``build``: Raise the build part.
.. option:: <VERSION>
The version to bump.
To bump a version, you pass the name of the part (``major``, ``minor``,
``patch``, ``prerelease``, or ``build``) and the version string.
The bumped version is printed on standard out::
$ pysemver bump major 1.2.3
2.0.0
$ pysemver bump minor 1.2.3
1.3.0
If you pass a version string which is not a valid semantical version,
you get an error message and a return code != 0::
$ pysemver bump build 1.5
ERROR 1.5 is not valid SemVer string
pysemver check
~~~~~~~~~~~~~~
Checks if a string is a valid semver version.
.. code:: bash
pysemver check <VERSION>
.. option:: <VERSION>
The version string to check.
The *error code* returned by the script indicates if the
version is valid (=0) or not (!=0)::
$ pysemver check 1.2.3; echo $?
0
$ pysemver check 2.1; echo $?
ERROR Invalid version '2.1'
2
pysemver compare
~~~~~~~~~~~~~~~~
Compare two versions.
.. code:: bash
pysemver compare <VERSION1> <VERSION2>
.. option:: <VERSION1>
First version
.. option:: <VERSION2>
Second version
When you compare two versions, the result is printed on *standard out*,
to indicates which is the bigger version:
* ``-1`` if first version is smaller than the second version,
* ``0`` if both versions are the same,
* ``1`` if the first version is greater than the second version.
Return Code
-----------
The *return code* of the script (accessible by ``$?`` from the Bash)
indicates if the subcommand returned successfully nor not. It is *not*
meant as the result of the subcommand.
The result of the subcommand is printed on the standard out channel
("stdout" or ``0``), any error messages to standard error ("stderr" or
``2``).
For example, to compare two versions, the command expects two valid
semver versions::
$ pysemver compare 1.2.3 2.4.0
-1
$ echo $?
0
The return code is zero, but the result is ``-1``.
However, if you pass invalid versions, you get this situation::
$ pysemver compare 1.2.3 2.4
ERROR 2.4 is not valid SemVer string
$ echo $?
2
If you use the :command:`pysemver` in your own scripts, check the
return code first before you process the standard output.
See also
--------
:Documentation: https://python-semver.readthedocs.io/
:Source code: https://github.com/python-semver/python-semver
:Bug tracker: https://github.com/python-semver/python-semver/issues
================================================
FILE: docs/readme.rst
================================================
If you are searching for how to stay compatible
with semver3, refer to :ref:`semver2-to-3`.
.. include:: ../README.rst
================================================
FILE: docs/requirements.txt
================================================
# Sphinx requirements file for documentation
# Required in .readthedocs.yaml
sphinx==7.0.1
sphinx-argparse==0.4.0
sphinx-autodoc-typehints==1.24.0
================================================
FILE: docs/usage/access-parts-of-a-version.rst
================================================
.. _sec.properties.parts:
Accessing Parts of a Version Through Names
==========================================
.. meta::
:description lang=en:
Accessing parts of a version through names
The :class:`~semver.version.Version` class contains attributes to access the different
parts of a version:
.. code-block:: python
>>> v = Version.parse("3.4.5-pre.2+build.4")
>>> v.major
3
>>> v.minor
4
>>> v.patch
5
>>> v.prerelease
'pre.2'
>>> v.build
'build.4'
However, the attributes are read-only. You cannot change any of the above attributes.
If you do, you get an :py:exc:`python:AttributeError`::
>>> v.minor = 5
Traceback (most recent call last):
...
AttributeError: attribute 'minor' is readonly
If you need to replace different parts of a version, refer to section :ref:`sec.replace.parts`.
In case you need the different parts of a version stepwise, iterate over the :class:`~semver.version.Version` instance::
>>> for item in Version.parse("3.4.5-pre.2+build.4"):
... print(item)
3
4
5
pre.2
build.4
>>> list(Version.parse("3.4.5-pre.2+build.4"))
[3, 4, 5, 'pre.2', 'build.4']
================================================
FILE: docs/usage/access-parts-through-index.rst
================================================
.. _sec.getitem.parts:
Accessing Parts Through Index Numbers
=====================================
.. meta::
:description lang=en:
Accessing parts through index numbers
.. versionadded:: 2.10.0
Another way to access parts of a version is to use an index notation. The underlying
:class:`~semver.version.Version` object allows to access its data through
the magic method :meth:`~semver.version.Version.__getitem__`.
For example, the ``major`` part can be accessed by index number 0 (zero).
Likewise the other parts:
.. code-block:: python
>>> ver = Version.parse("10.3.2-pre.5+build.10")
>>> ver[0], ver[1], ver[2], ver[3], ver[4]
(10, 3, 2, 'pre.5', 'build.10')
If you need more than one part at the same time, use the slice notation:
.. code-block:: python
>>> ver[0:3]
(10, 3, 2)
Or, as an alternative, you can pass a :func:`slice` object:
.. code-block:: python
>>> sl = slice(0,3)
>>> ver[sl]
(10, 3, 2)
Negative numbers or undefined parts raise an :py:exc:`python:IndexError` exception:
.. code-block:: python
>>> ver = Version.parse("10.3.2")
>>> ver[3]
Traceback (most recent call last):
...
IndexError: Version part undefined
>>> ver[-2]
Traceback (most recent call last):
...
IndexError: Version index cannot be negative
================================================
FILE: docs/usage/check-compatible-semver-version.rst
================================================
Checking for a Compatible Semver Version
========================================
.. meta::
:description lang=en:
Check for a compatible semver version
To check if a *change* from a semver version ``a`` to a semver
version ``b`` is *compatible* according to semver rule, use the method
:meth:`~semver.version.Version.is_compatible`.
The expression ``a.is_compatible(b) is True`` if one of the following
statements is true:
* both versions are equal, or
* both majors are equal and higher than 0. The same applies for both
minor parts. Both pre-releases are equal, or
* both majors are equal and higher than 0. The minor of ``b``'s
minor version is higher then ``a``'s. Both pre-releases are equal.
In all other cases, the result is false.
Keep in mind, the method *does not* check patches!
* Two different majors:
.. code-block:: python
>>> a = Version(1, 1, 1)
>>> b = Version(2, 0, 0)
>>> a.is_compatible(b)
False
>>> b.is_compatible(a)
False
* Two different minors:
.. code-block:: python
>>> a = Version(1, 1, 0)
>>> b = Version(1, 0, 0)
>>> a.is_compatible(b)
False
>>> b.is_compatible(a)
True
* The same two majors and minors:
.. code-block:: python
>>> a = Version(1, 1, 1)
>>> b = Version(1, 1, 0)
>>> a.is_compatible(b)
True
>>> b.is_compatible(a)
True
* Release and pre-release:
.. code-block:: python
>>> a = Version(1, 1, 1)
>>> b = Version(1, 0, 0,'rc1')
>>> a.is_compatible(b)
False
>>> b.is_compatible(a)
False
* Different pre-releases:
.. code-block:: python
>>> a = Version(1, 0, 0, 'rc1')
>>> b = Version(1, 0, 0, 'rc2')
>>> a.is_compatible(b)
False
>>> b.is_compatible(a)
False
* Identical pre-releases:
.. code-block:: python
>>> a = Version(1, 0, 0,'rc1')
>>> b = Version(1, 0, 0,'rc1')
>>> a.is_compatible(b)
True
* All major zero versions are incompatible with anything but itself:
.. code-block:: python
>>> Version(0, 1, 0).is_compatible(Version(0, 1, 1))
False
# Only identical versions are compatible for major zero versions:
>>> Version(0, 1, 0).is_compatible(Version(0, 1, 0))
True
================================================
FILE: docs/usage/check-valid-semver-version.rst
================================================
Checking for a Valid Semver Version
===================================
.. meta::
:description lang=en:
Checking for a valid semver version
If you need to check a string if it is a valid semver version, use the
classmethod :meth:`~semver.version.Version.is_valid`:
.. code-block:: python
>>> Version.is_valid("1.0.0")
True
>>> Version.is_valid("invalid")
False
================================================
FILE: docs/usage/compare-versions-through-expression.rst
================================================
Comparing Versions through an Expression
========================================
.. meta::
:description lang=en:
Comparing versions through an expression
If you need a more fine-grained approach of comparing two versions,
use the :meth:`~semver.version.Version.match` function. It expects two arguments:
1. a version string
2. a match expression
Currently, the match expression supports the following operators:
* ``<`` smaller than
* ``>`` greater than
* ``>=`` greater or equal than
* ``<=`` smaller or equal than
* ``==`` equal
* ``!=`` not equal
That gives you the following possibilities to express your condition:
.. code-block:: python
>>> Version.parse("2.0.0").match(">=1.0.0")
True
>>> Version.parse("1.0.0").match(">1.0.0")
False
If no operator is specified, the match expression is interpreted as a
version to be compared for equality. This allows handling the common
case of version compatibility checking through either an exact version
or a match expression very easy to implement, as the same code will
handle both cases:
.. code-block:: python
>>> Version.parse("2.0.0").match("2.0.0")
True
>>> Version.parse("1.0.0").match("3.5.1")
False
================================================
FILE: docs/usage/compare-versions.rst
================================================
Comparing Versions
==================
.. meta::
:description lang=en:
Comparing versions with semver.compare and the Version class
To compare two versions depends on your type:
* **Two strings**
Use :func:`semver.compare <semver._deprecated.compare>`::
>>> semver.compare("1.0.0", "2.0.0")
-1
>>> semver.compare("2.0.0", "1.0.0")
1
>>> semver.compare("2.0.0", "2.0.0")
0
The return value is negative if ``version1 < version2``, zero if
``version1 == version2`` and strictly positive if ``version1 > version2``.
* **Two** :class:`~semver.version.Version` **instances**
Use the specific operator. Currently, the operators ``<``,
``<=``, ``>``, ``>=``, ``==``, and ``!=`` are supported::
>>> v1 = Version.parse("3.4.5")
>>> v2 = Version.parse("3.5.1")
>>> v1 < v2
True
>>> v1 > v2
False
* **A** :class:`~semver.version.Version` **type and a** :func:`tuple` **or** :func:`list`
Use the operator as with two :class:`~semver.version.Version` types::
>>> v = Version.parse("3.4.5")
>>> v > (1, 0)
True
>>> v < [3, 5]
True
The opposite does also work::
>>> (1, 0) < v
True
>>> [3, 5] > v
True
* **A** :class:`~semver.version.Version` **type and a** :func:`str`
You can use also raw strings to compare::
>>> v > "1.0.0"
True
>>> v < "3.5.0"
True
The opposite does also work::
>>> "1.0.0" < v
True
>>> "3.5.0" > v
True
However, if you compare incomplete strings, you get a :py:exc:`python:ValueError` exception::
>>> v > "1.0"
Traceback (most recent call last):
...
ValueError: 1.0 is not valid SemVer string
* **A** :class:`~semver.version.Version` **type and a** :func:`dict`
You can also use a dictionary. In contrast to strings, you can have an "incomplete"
version (as the other parts are set to zero)::
>>> v > dict(major=1)
True
The opposite does also work::
>>> dict(major=1) < v
True
If the dictionary contains unknown keys, you get a :py:exc:`python:TypeError` exception::
>>> v > dict(major=1, unknown=42)
Traceback (most recent call last):
...
TypeError: ... got an unexpected keyword argument 'unknown'
Other types cannot be compared.
If you need to convert some types into others, refer to :ref:`sec.convert.versions`.
The use of these comparison operators also implies that you can use builtin
functions that leverage this capability; builtins including, but not limited to: :func:`max`, :func:`min`
(for examples, see :ref:`sec_max_min`) and :func:`sorted`.
================================================
FILE: docs/usage/convert-version-into-different-types.rst
================================================
.. _sec.convert.versions:
Converting a Version instance into Different Types
==================================================
.. meta::
:description lang=en:
Converting a version instance into different types
Sometimes it is needed to convert a :class:`~semver.version.Version` instance into
a different type. For example, for displaying or to access all parts.
It is possible to convert a :class:`~semver.version.Version` instance:
* Into a string with the builtin function :func:`str`::
>>> str(Version.parse("3.4.5-pre.2+build.4"))
'3.4.5-pre.2+build.4'
* Into a dictionary with :meth:`~semver.version.Version.to_dict`::
>>> v = Version(major=3, minor=4, patch=5)
>>> v.to_dict()
{'major': 3, 'minor': 4, 'patch': 5, 'prerelease': None, 'build': None}
* Into a tuple with :meth:`~semver.version.Version.to_tuple`::
>>> v = Version(major=5, minor=4, patch=2)
>>> v.to_tuple()
(5, 4, 2, None, None)
================================================
FILE: docs/usage/create-a-version.rst
================================================
Creating a Version
==================
.. meta::
:description lang=en:
Creating a version using different methods
.. versionchanged:: 3.0.0
The former :class:`~semver.version.VersionInfo` class
has been renamed to :class:`~semver.version.Version`.
The preferred way to create a new version is with the class
:class:`~semver.version.Version`.
.. note::
In the previous major release semver 2 it was possible to
create a version with module level functions.
However, module level functions are marked as *deprecated*
since version 2.x.y now.
These functions will be removed.
For details, see the sections :ref:`sec_replace_deprecated_functions`
and :ref:`sec_display_deprecation_warnings`.
A :class:`~semver.version.Version` instance can be created in different ways:
* From a Unicode string::
>>> from semver.version import Version
>>> Version.parse("3.4.5-pre.2+build.4")
Version(major=3, minor=4, patch=5, prerelease='pre.2', build='build.4')
>>> Version.parse(u"5.3.1")
Version(major=5, minor=3, patch=1, prerelease=None, build=None)
* From a byte string::
>>> Version.parse(b"2.3.4")
Version(major=2, minor=3, patch=4, prerelease=None, build=None)
* From individual parts by a dictionary::
>>> d = {'major': 3, 'minor': 4, 'patch': 5, 'prerelease': 'pre.2', 'build': 'build.4'}
>>> Version(**d)
Version(major=3, minor=4, patch=5, prerelease='pre.2', build='build.4')
Keep in mind, the ``major``, ``minor``, ``patch`` parts has to
be positive integers or strings, otherwise a :py:exc:`python:ValueError` is raised:
>>> d = {'major': -3, 'minor': 4, 'patch': 5, 'prerelease': 'pre.2', 'build': 'build.4'}
>>> Version(**d)
Traceback (most recent call last):
...
ValueError: 'major' is negative. A version can only be positive.
As a minimum requirement, your dictionary needs at least the ``major``
key, others can be omitted. You get a :py:exc:`python:TypeError` if your
dictionary contains invalid keys.
Only the keys ``major``, ``minor``, ``patch``, ``prerelease``, and ``build``
are allowed.
* From a tuple::
>>> t = (3, 5, 6)
>>> Version(*t)
Version(major=3, minor=5, patch=6, prerelease=None, build=None)
You can pass either an integer or a string for ``major``, ``minor``, or
``patch``::
>>> Version("3", "5", 6)
Version(major=3, minor=5, patch=6, prerelease=None, build=None)
The old, deprecated module level functions are still available but
using them are discoraged. They are available to convert old code
to semver3.
If you need them, they return different builtin objects (string and dictionary).
Keep in mind, once you have converted a version into a string or dictionary,
it's an ordinary builtin object. It's not a special version object like
the :class:`~semver.version.Version` class anymore.
Depending on your use case, the following methods are available:
* From individual version parts into a string
In some cases you only need a string from your version data::
>>> semver.format_version(3, 4, 5, 'pre.2', 'build.4')
'3.4.5-pre.2+build.4'
* From a string into a dictionary
To access individual parts, you can use the function :func:`~semver._deprecated.parse`::
>>> semver.parse("3.4.5-pre.2+build.4")
{'major': 3, 'minor': 4, 'patch': 5, 'prerelease': 'pre.2', 'build': 'build.4'}
If you pass an invalid version string you will get a :py:exc:`python:ValueError`::
>>> semver.parse("1.2")
Traceback (most recent call last):
...
ValueError: 1.2 is not valid SemVer string
================================================
FILE: docs/usage/determine-version-equality.rst
================================================
Determining Version Equality
============================
.. meta::
:description lang=en:
Determining verison equality
Version equality means for semver, that major, minor, patch, and prerelease
parts are equal in both versions you compare. The build part is ignored.
For example::
>>> v = Version.parse("1.2.3-rc4+1e4664d")
>>> v == "1.2.3-rc4+dedbeef"
True
This also applies when a :class:`Version <semver.version.Version>` is a member of a set, or a
dictionary key::
>>> d = {}
>>> v1 = Version.parse("1.2.3-rc4+1e4664d")
>>> v2 = Version.parse("1.2.3-rc4+dedbeef")
>>> d[v1] = 1
>>> d[v2]
1
>>> s = set()
>>> s.add(v1)
>>> v2 in s
True
================================================
FILE: docs/usage/get-min-and-max-of-multiple-versions.rst
================================================
.. _sec_max_min:
Getting Minimum and Maximum of Multiple Versions
================================================
.. meta::
:description lang=en:
Getting minimum and maximum of multiple versions
.. versionchanged:: 2.10.2
The functions :func:`semver.max_ver` and :func:`semver.min_ver` are deprecated in
favor of their builtin counterparts :func:`max` and :func:`min`.
Since :class:`~semver.version.Version` implements
:meth:`~semver.version.Version.__gt__` and
:meth:`~semver.version.Version.__lt__`, it can be used with builtins requiring:
.. code-block:: python
>>> max([Version(0, 1, 0), Version(0, 2, 0), Version(0, 1, 3)])
Version(major=0, minor=2, patch=0, prerelease=None, build=None)
>>> min([Version(0, 1, 0), Version(0, 2, 0), Version(0, 1, 3)])
Version(major=0, minor=1, patch=0, prerelease=None, build=None)
Incidentally, using :func:`map`, you can get the min or max version of any number of versions of the same type
(convertible to :class:`~semver.version.Version`).
For example, here are the maximum and minimum versions of a list of version strings:
.. code-block:: python
>>> max(['1.1.0', '1.2.0', '2.1.0', '0.5.10', '0.4.99'], key=Version.parse)
'2.1.0'
>>> min(['1.1.0', '1.2.0', '2.1.0', '0.5.10', '0.4.99'], key=Version.parse)
'0.4.99'
And the same can be done with tuples:
.. code-block:: python
>>> max(map(lambda v: Version(*v), [(1, 1, 0), (1, 2, 0), (2, 1, 0), (0, 5, 10), (0, 4, 99)])).to_tuple()
(2, 1, 0, None, None)
>>> min(map(lambda v: Version(*v), [(1, 1, 0), (1, 2, 0), (2, 1, 0), (0, 5, 10), (0, 4, 99)])).to_tuple()
(0, 4, 99, None, None)
For dictionaries, it is very similar to finding the max version tuple: see :ref:`sec.convert.versions`.
================================================
FILE: docs/usage/increase-parts-of-a-version_prereleases.rst
================================================
.. _increase-parts-of-a-version:
Increasing Parts of a Version Taking into Account Prereleases
=============================================================
.. versionadded:: 2.10.0
Added :meth:`~semver.version.Version.next_version`.
If you want to raise your version and take prereleases into account,
the function :meth:`~semver.version.Version.next_version`
would perhaps a better fit.
.. code-block:: python
>>> v = Version.parse("3.4.5-pre.2+build.4")
>>> str(v.next_version(part="prerelease"))
'3.4.5-pre.3'
>>> str(Version.parse("3.4.5-pre.2+build.4").next_version(part="patch"))
'3.4.5'
>>> str(Version.parse("3.4.5+build.4").next_version(part="patch"))
'3.4.5'
>>> str(Version.parse("0.1.4").next_version("prerelease"))
'0.1.5-rc.1'
================================================
FILE: docs/usage/index.rst
================================================
Using semver
============
.. toctree::
semver_org-version
semver-version
create-a-version
parse-version-string
check-valid-semver-version
check-compatible-semver-version
access-parts-of-a-version
access-parts-through-index
replace-parts-of-a-version
convert-version-into-different-types
raise-parts-of-a-version
increase-parts-of-a-version_prereleases
compare-versions
determine-version-equality
compare-versions-through-expression
get-min-and-max-of-multiple-versions
================================================
FILE: docs/usage/parse-version-string.rst
================================================
Parsing a Version String
========================
"Parsing" in this context means to identify the different parts in a string.
Use the function :meth:`~semver.version.Version.parse`::
>>> Version.parse("3.4.5-pre.2+build.4")
Version(major=3, minor=4, patch=5, prerelease='pre.2', build='build.4')
Set the parameter ``optional_minor_and_patch=True`` to allow optional
minor and patch parts. Optional parts are set to zero. By default (False), the
version string to parse has to follow the semver specification::
>>> Version.parse("1.2", optional_minor_and_patch=True)
Version(major=1, minor=2, patch=0, prerelease=None, build=None)
================================================
FILE: docs/usage/raise-parts-of-a-version.rst
================================================
Raising Parts of a Version
==========================
.. note::
Keep in mind, by default, "raising" the pre-release for a version without an existing
prerelease part, only will make your complete version *lower* than before.
For example, having version ``1.0.0`` and raising the pre-release
will lead to ``1.0.0-rc.1``, but ``1.0.0-rc.1`` is smaller than ``1.0.0``.
To avoid this, set `bump_when_empty=True` in the
:meth:`~semver.version.Version.bump_prerelease` method, or by using the
method :meth:`~semver.version.Version.next_version`
in section :ref:`increase-parts-of-a-version`.
The ``semver`` module contains the following functions to raise parts of
a version:
* :meth:`~semver.version.Version.bump_major`: raises the major part and set all other parts to
zero. Set ``prerelease`` and ``build`` to ``None``.
* :meth:`~semver.version.Version.bump_minor`: raises the minor part and sets ``patch`` to zero.
Set ``prerelease`` and ``build`` to ``None``.
* :meth:`~semver.version.Version.bump_patch`: raises the patch part. Set ``prerelease`` and
``build`` to ``None``.
* :meth:`~semver.version.Version.bump_prerelease`: raises the prerelease part and set
``build`` to ``None``.
* :meth:`~semver.version.Version.bump_build`: raises the build part.
.. code-block:: python
>>> str(Version.parse("3.4.5-pre.2+build.4").bump_major())
'4.0.0'
>>> str(Version.parse("3.4.5-pre.2+build.4").bump_minor())
'3.5.0'
>>> str(Version.parse("3.4.5-pre.2+build.4").bump_patch())
'3.4.6'
>>> str(Version.parse("3.4.5-pre.2+build.4").bump_prerelease())
'3.4.5-pre.3'
>>> str(Version.parse("3.4.5-pre.2+build.4").bump_build())
'3.4.5-pre.2+build.5'
Likewise the module level functions :func:`semver.bump_major`.
For the methods :meth:`~semver.version.Version.bump_prerelease`
and :meth:`~semver.version.Version.bump_build` it's possible to pass an empty string or ``None``.
However, it gives different results:
.. code-block:: python
>>> str(Version.parse("3.4.5").bump_prerelease(''))
'3.4.5-1'
>>> str(Version.parse("3.4.5").bump_prerelease(None))
'3.4.5-rc.1'
An empty string removes any prefix whereas ``None`` is the same as calling
the method without any argument.
If you already have a prerelease, the argument for the method
is not taken into account:
.. code-block:: python
>>> str(Version.parse("3.4.5-rc.1").bump_prerelease(None))
'3.4.5-rc.2'
>>> str(Version.parse("3.4.5-rc.1").bump_prerelease(''))
'3.4.5-rc.2'
To ensure correct ordering, we append `.0` to the last prerelease identifier
if it's not numeric. This prevents cases where `rc9` would incorrectly sort
lower than `rc10` (non-numeric identifiers are compared alphabetically):
.. code-block:: python
>>> str(Version.parse("3.4.5-rc9").bump_prerelease())
'3.4.5-rc9.0'
>>> str(Version.parse("3.4.5-rc.9").bump_prerelease())
'3.4.5-rc.10'
================================================
FILE: docs/usage/replace-parts-of-a-version.rst
================================================
.. _sec.replace.parts:
Replacing Parts of a Version
============================
If you want to replace different parts of a version, but leave other parts
unmodified, use the function :meth:`~semver.version.Version.replace`:
>>> version = semver.Version.parse("1.4.5-pre.1+build.6")
>>> version.replace(major=2, minor=2)
Version(major=2, minor=2, patch=5, prerelease='pre.1', build='build.6')
If you pass invalid keys you get an exception::
>>> version = semver.Version.parse("1.4.5-pre.1+build.6")
>>> version.replace(invalidkey=2)
Traceback (most recent call last):
...
TypeError: replace() got 1 unexpected keyword argument(s): invalidkey
================================================
FILE: docs/usage/semver-version.rst
================================================
Getting the Version of semver
=============================
To know the version of semver itself, use the following construct::
>>> semver.__version__
'3.0.4'
================================================
FILE: docs/usage/semver_org-version.rst
================================================
Getting the Implemented semver.org Version
==========================================
The semver.org page is the authoritative specification of how semantic
versioning is defined.
To know which version of semver.org is implemented in the semver library,
use the following constant::
>>> semver.SEMVER_SPEC_VERSION
'2.0.0'
================================================
FILE: docs/version-policy.rst
================================================
.. _version-policy:
Version Policy
==============
.. |MAINT| replace:: ``maint/v2``
.. _MAINT: https://github.com/python-semver/python-semver/tree/maint/v2
.. |CHANGELOG| replace:: ``Changelog``
.. _CHANGELOG: https://github.com/python-semver/python-semver/blob/maint/v2/CHANGELOG.rst
The move from v2 to v3 introduced many changes and deprecated module functions.
The main functionality is handled by the :class:`~semver.version.Version` class
now. Find more information in the section :ref:`semver2-to-3`.
semver Version 2
----------------
Active development of major version 2 has stopped. No new features nor
backports will be integrated.
We recommend to upgrade your workflow to Python 3 to gain support,
bugfixes, and new features.
If you still need this old version, use the |MAINT|_ branch. There you
can look for the |CHANGELOG|_ if you need some details about the history.
semver Version 3
----------------
We will not intentionally make breaking changes in minor releases of V3.
Methods marked as ``deprecated`` raise a warning message when used from the
:py:mod:`python:warnings` module.
Refer to section :ref:`sec_display_deprecation_warnings` to get more information about how to customize it.
Check section :ref:`sec_replace_deprecated_functions` to make your code
ready for future major releases.
semver Version 3 and beyond
---------------------------
Methods that were marked as deprecated will be very likely be removed.
Support for Python versions
---------------------------
This project will drop support for a Python version when the
following conditions are met:
* The Python version has reached `EOL <https://devguide.python.org/versions/>`_.
================================================
FILE: pyproject.toml
================================================
#
#
# See also https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html
#
# General idea taken from
# https://godatadriven.com/blog/a-practical-guide-to-setuptools-and-pyproject-toml/
[build-system]
requires = [
"setuptools>=61",
# "setuptools-scm>=8",
]
build-backend = "setuptools.build_meta"
[project]
requires-python = ">=3.7"
name = "semver"
description = "Python helper for Semantic Versioning (https://semver.org)"
readme = "README.rst"
# PEP 639
# licence = "BSD-2-Clause"
# readme.content-type = "text/x-rst"
license = { file = "LICENSE.txt" }
authors = [
{name = "Kostiantyn Rybnikov", email = "k-bx@k-bx.com"},
{name = "Tom Schraitle", email = "toms@suse.de"},
]
maintainers = [
{name = "Tom Schraitle", email = "toms@suse.de"},
{name = "Sebastien Celles", email = "s.celles@gmail.com"},
]
dynamic = ["version",]
classifiers = [
"Environment :: Web Environment",
"Intended Audience :: Developers",
"Development Status :: 5 - Production/Stable",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Software Development :: Libraries :: Python Modules",
]
[project.urls]
"GitHub Homepage" = "https://github.com/python-semver/python-semver"
Changelog = "https://python-semver.readthedocs.io/en/latest/changelog.html"
Documentation = "https://python-semver.rtfd.io"
Releases = "https://github.com/python-semver/python-semver/releases"
"Bug Tracker" = "https://github.com/python-semver/python-semver/issues"
[project.scripts]
pysemver = "semver.cli:main"
[dependency-groups]
# See https://github.com/astral-sh/uv/issues/5632
# See https://peps.python.org/pep-0735/
typing = [
"mypy",
"pyright",
]
formatting = [
"black",
"ruff",
"docformatter",
]
test = [
"tox",
"pytest",
"pytest-cov",
]
publish = [
"twine",
# pyproject-build is required for building the package
"build",
]
changelog = [
"towncrier",
]
docs = [
# Required in .readthedocs.yaml
"sphinx", # ==7.0.1
"sphinx-argparse", # ==0.4.0
"sphinx-autodoc-typehints", # ==1.24.0
"restview",
]
devel = [
{include-group = "typing"},
{include-group = "formatting"},
{include-group = "test"},
{include-group = "changelog"},
{include-group = "docs"},
{include-group = "publish"},
]
# Only needed when using GitHub Actions
gh-action = [
{include-group = "devel"},
"tox-gh-actions",
]
# --- Setuptools configuration ---
[tool.setuptools]
package-dir = {"" = "src"}
package-data = { "semver" = ["py.typed"] }
include-package-data = true
[tool.setuptools.packages.find]
# Is that still necessary?
# list of folders that contain the packages (["."] by default)
where = ["src"]
[tool.setuptools.dynamic]
version = {attr = "semver.__about__.__version__"}
# ---------------------------------
[tool.setuptools_scm]
version_scheme = "post-release"
local_scheme = "dirty-tag"
[tool.mypy]
# ignore_missing_imports = true
check_untyped_defs = true
show_error_codes = true
# strict = true
pretty = true
[tool.black]
line-length = 88
target-version = ['py37', 'py38', 'py39', 'py310', 'py311']
# diff = true
[tool.docformatter]
wrap-summaries = 80
close-quotes-on-newline = true
# make-summary-multi-line = true
black = true
pre-summary-newline = true
recursive = true
[tool.towncrier]
package = "semver"
package_dir = "src"
filename = "CHANGELOG.rst"
directory = "changelog.d/"
title_format = "Version {version}"
template = "changelog.d/_template.rst"
# issue_format = "`#{issue} <https://github.com/python-attrs/attrs/issues/{issue}>`_"
# issue_format = ":gh:`{issue}`"
[tool.towncrier.fragment.breaking]
name = "Breaking Changes"
[tool.towncrier.fragment.bugfix]
name = "Bug Fixes"
[tool.towncrier.fragment.deprecation]
name = "Deprecations"
[tool.towncrier.fragment.doc]
name = "Improved Documentation"
[tool.towncrier.fragment.feature]
name = "Features"
[tool.towncrier.fragment.removal]
name = "Removals"
[tool.towncrier.fragment.trivial]
name = "Trivial Changes"
[tool.towncrier.fragment.internal]
name = "Internal Changes"
================================================
FILE: release-procedure.md
================================================
# Release Procedure
The following procedures gives a short overview of what steps are needed to
create a new release.
## Prepare your environment
1. Create your API tokens:
1. From the [PyPI test server](https://test.pypi.org/manage/account/token/).
1. From the official [PyPI server](https://test.pypi.org/manage/account/token/).
1. Save both tokens it in a safe place like your password manager.
1. Create a file `~/.pypirc` with file mode 0600 and the following minimal content:
# Protect the file with chmod 0600 ~/.pypirc
[distutils]
index-servers =
test-semver
semver
[test-semver]
repository = https://test.pypi.org/legacy/
username = __token__
password = <YOUR_TEST_API_TOKEN>
[semver]
repository = https://pypi.org/legacy/
username = __token__
password = <YOUR_API_TOKEN>
1. Install uv as shown in Astral's [Installing uv](https://docs.astral.sh/uv/getting-started/installation/) documentation.
1. Update the project's environment:
uv sync --group devel
1. Activate your environment:
source .venv/bin/activate
## Prepare the Release
1. Create a new branch `release/<VERSION>`.
1. If one or several supported **Python** versions have been removed or added, verify that the following files have been updated:
* `pyproject.toml` (look into the key `project.requires-python` and `project.classifiers`)
* `tox.ini`
* `.git/workflows/pythonpackage.yml`
* `CITATION.cff`
1. Verify that:
* the version has been updated and follow <https://semver.org>:
* `src/semver/__about__.py`
* `docs/usage/semver-version.rst`
* all issues for a new release are closed: <https://github.com/python-semver/python-semver/issues>.
* all pull requests that should be included in this release are merged: <https://github.com/python-semver/python-semver/pulls>.
* continuous integration for latest build was passing:
<https://github.com/python-semver/python-semver/actions>.
1. Add eventually new contributor(s) to [CONTRIBUTORS](https://github.com/python-semver/python-semver/blob/master/CONTRIBUTORS).
1. Create the changelog:
1. Check if all changelog entries are created. If some are missing, [create them](https://python-semver.readthedocs.io/en/latest/development.html#adding-a-changelog-entry).
1. Show the new draft [CHANGELOG](https://github.com/python-semver/python-semver/blob/master/CHANGELOG.rst) entry for the latest release with:
uvx tox r -e changelog
1. Check the output. If you are not happy, update the files in the
`changelog.d/` directory.
If everything is okay, build the new `CHANGELOG` with:
uvx tox r -e changelog -- build
1. Build the documentation and check the output:
uvx tox r -e docs
1. Commit all changes, push, and create a pull request.
## Create the New Release
1. Ensure that long description ([README.rst](https://github.com/python-semver/python-semver/blob/master/README.rst)) can be correctly rendered by Pypi using `uvx restview -b README.rst`
1. Clean up your local Git repository. Be careful,
as it **will remove all files** which are not
versioned by Git:
git clean -xfd
Before you create your distribution files, clean
the directory too:
rm dist/*
1. Create the distribution files (wheel and source):
uvx tox r -e prepare-dist
1. Upload the wheel and source to TestPyPI first:
twine upload --verbose --repository test-semver dist/*
(Normally you would do it with `uv publish`, but for some unknown reason it didn't work for me.)
1. Check if everything is okay with the wheel.
Check also the web site `https://test.pypi.org/project/<VERSION>/`
# Finish the release
1. If everything looks fine, merge the pull request.
1. Upload to PyPI:
$ git clean -xfd
$ tox r -e prepare-dist
$ twine upload --verbose --repository semver dist/*
1. Go to https://pypi.org/project/semver/ to verify that new version is online and the page is rendered correctly.
1. Create a tag:
git tag -a x.y.z
It's recommended to use the generated Tox output from the Changelog.
1. Push the tag:
git push origin x.y.z
1. In [GitHub Release page](https://github.com/python-semver/python-semver/release) document the new release.
Select the tag from the last step and copy the content of the tag description into the release description.
1. Announce it in <https://github.com/python-semver/python-semver/discussions/categories/announcements>.
You're done! Celebrate!
================================================
FILE: setup.cfg
================================================
#
# Metadata for setup.py
#
# See https://setuptools.rtfd.io/en/latest/userguide/declarative_config.html
[pycodestyle]
count = False
ignore = E203,E701
# E226,E302,E41
max-line-length = 88
statistics = True
exclude =
src/semver/__init__.py
.env,
.eggs,
.tox,
.git,
docs
================================================
FILE: src/semver/__about__.py
================================================
"""
Metadata about semver.
Contains information about semver's version, the implemented version
of the semver specifictation, author, maintainers, and description.
.. autodata:: __author__
.. autodata:: __description__
.. autodata:: __maintainer__
.. autodata:: __version__
.. autodata:: SEMVER_SPEC_VERSION
"""
#: Semver version
__version__ = "3.0.4"
#: Original semver author
__author__ = "Kostiantyn Rybnikov"
#: Author's email address
__author_email__ = "k-bx@k-bx.com"
#: Current maintainer
__maintainer__ = ["Sebastien Celles", "Tom Schraitle"]
#: Maintainer's email address
__maintainer_email__ = "s.celles@gmail.com"
#: Short description about semver
__description__ = "Python helper for Semantic Versioning (https://semver.org)"
#: Supported semver specification
SEMVER_SPEC_VERSION = "2.0.0"
================================================
FILE: src/semver/__init__.py
================================================
"""
Semver package major release 3.
A Python module for semantic versioning. Simplifies comparing versions.
"""
from ._deprecated import (
bump_build,
bump_major,
bump_minor,
bump_patch,
compare,
bump_prerelease,
finalize_version,
format_version,
match,
max_ver,
min_ver,
parse,
parse_version_info,
replace,
cmd_bump,
cmd_compare,
cmd_nextver,
cmd_check,
createparser,
process,
main,
)
from .version import Version, VersionInfo
from .__about__ import (
__version__,
__author__,
__maintainer__,
__author_email__,
__description__,
__maintainer_email__,
SEMVER_SPEC_VERSION,
)
__all__ = [
"bump_build",
"bump_major",
"bump_minor",
"bump_patch",
"compare",
"bump_prerelease",
"finalize_version",
"format_version",
"match",
"max_ver",
"min_ver",
"parse",
"parse_version_info",
"replace",
"cmd_bump",
"cmd_compare",
"cmd_nextver",
"cmd_check",
"createparser",
"process",
"main",
"Version",
"VersionInfo",
"__version__",
"__author__",
"__maintainer__",
"__author_email__",
"__description__",
"__maintainer_email__",
"SEMVER_SPEC_VERSION",
]
================================================
FILE: src/semver/__main__.py
================================================
"""
Module to support call with :file:`__main__.py`. Used to support the following
call::
$ python3 -m semver ...
This makes it also possible to "run" a wheel like in this command::
$ python3 semver-3*-py3-none-any.whl/semver -h
"""
import os.path
import sys
from typing import List, Optional
from semver import cli
def main(cliargs: Optional[List[str]] = None) -> int:
if __package__ == "":
path = os.path.dirname(os.path.dirname(__file__))
sys.path[0:0] = [path]
return cli.main(cliargs)
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
================================================
FILE: src/semver/_deprecated.py
================================================
"""
Contains all deprecated functions.
.. autofunction: deprecated
"""
import inspect
import warnings
from functools import partial, wraps
from types import FrameType
from typing import Type, Callable, Optional, cast
from . import cli
from .version import Version
from ._types import Decorator, F
def deprecated(
func: Optional[F] = None,
*,
replace: Optional[str] = None,
version: Optional[str] = None,
remove: Optional[str] = None,
category: Type[Warning] = DeprecationWarning,
) -> Decorator:
"""
Decorates a function to output a deprecation warning.
:param func: the function to decorate
:param replace: the function to replace (use the full qualified
name like ``semver.version.Version.bump_major``.
:param version: the first version when this function was deprecated.
:param category: allow you to specify the deprecation warning class
of your choice. By default, it's :class:`DeprecationWarning`, but
you can choose :class:`PendingDeprecationWarning` or a custom class.
:return: decorated function which is marked as deprecated
"""
if func is None:
return partial(
deprecated,
replace=replace,
version=version,
remove=remove,
category=category,
)
@wraps(func)
def wrapper(*args, **kwargs) -> Callable[..., F]:
msg_list = ["Function 'semver.{f}' is deprecated."]
if version:
msg_list.append("Deprecated since version {v}. ")
if not remove:
msg_list.append("This function will be removed in semver 3.")
else:
msg_list.append(str(remove))
if replace:
msg_list.append("Use {r!r} instead.")
else:
msg_list.append("Use the respective 'semver.Version.{r}' instead.")
f = cast(F, func).__qualname__
r = replace or f
frame = cast(FrameType, cast(FrameType, inspect.currentframe()).f_back)
msg = " ".join(msg_list)
warnings.warn_explicit(
msg.format(f=f, r=r, v=version),
category=category,
filename=inspect.getfile(frame.f_code),
lineno=frame.f_lineno,
)
# As recommended in the Python documentation
# https://docs.python.org/3/library/inspect.html#the-interpreter-stack
# better remove the interpreter stack:
del frame
return func(*args, **kwargs) # type: ignore
return wrapper
@deprecated(
version="3.0.0",
remove="Still under investigation, see #258.",
category=PendingDeprecationWarning,
)
def compare(ver1: str, ver2: str) -> int:
"""
Compare two versions strings.
.. deprecated:: 3.0.0
The situation of this function is unclear and it might
disappear in the future.
If possible, use :meth:`semver.version.Version.compare`.
See :gh:`258` for details.
:param ver1: first version string
:param ver2: second version string
:return: The return value is negative if ver1 < ver2,
zero if ver1 == ver2 and strictly positive if ver1 > ver2
>>> semver.compare("1.0.0", "2.0.0")
-1
>>> semver.compare("2.0.0", "1.0.0")
1
>>> semver.compare("2.0.0", "2.0.0")
0
"""
return Version.parse(ver1).compare(ver2)
@deprecated(version="2.10.0")
def parse(version):
"""
Parse version to major, minor, patch, pre-release, build parts.
.. deprecated:: 2.10.0
Use :meth:`~semver.version.Version.parse` instead.
:param version: version string
:return: dictionary with the keys 'build', 'major', 'minor', 'patch',
and 'prerelease'. The prerelease or build keys can be None
if not provided
:rtype: dict
>>> ver = semver.parse('3.4.5-pre.2+build.4')
>>> ver['major']
3
>>> ver['minor']
4
>>> ver['patch']
5
>>> ver['prerelease']
'pre.2'
>>> ver['build']
'build.4'
"""
return Version.parse(version).to_dict()
@deprecated(replace="semver.version.Version.parse", version="2.10.0")
def parse_version_info(version):
"""
Parse version string to a Version instance.
.. deprecated:: 2.10.0
Use :meth:`~semver.version.Version.parse` instead.
.. versionadded:: 2.7.2
Added :func:`semver.parse_version_info`
:param version: version string
:return: a :class:`VersionInfo` instance
>>> version_info = semver.Version.parse("3.4.5-pre.2+build.4")
>>> version_info.major
3
>>> version_info.minor
4
>>> version_info.patch
5
>>> version_info.prerelease
'pre.2'
>>> version_info.build
'build.4'
"""
return Version.parse(version)
@deprecated(version="2.10.0")
def match(version, match_expr):
"""
Compare two versions strings through a comparison.
.. deprecated:: 2.10.0
Use :meth:`~semver.version.Version.match` instead.
:param str version: a version string
:param str match_expr: operator and version; valid operators are
< smaller than
> greater than
>= greator or equal than
<= smaller or equal than
== equal
!= not equal
:return: True if the expression matches the version, otherwise False
:rtype: bool
>>> semver.match("2.0.0", ">=1.0.0")
True
>>> semver.match("1.0.0", ">1.0.0")
False
"""
ver = Version.parse(version)
return ver.match(match_expr)
@deprecated(replace="max", version="2.10.2")
def max_ver(ver1, ver2):
"""
Returns the greater version of two versions strings.
.. deprecated:: 2.10.2
Use :func:`max` instead.
:param ver1: version string 1
:param ver2: version string 2
:return: the greater version of the two
:rtype: :class:`Version`
>>> semver.max_ver("1.0.0", "2.0.0")
'2.0.0'
"""
return str(max(ver1, ver2, key=Version.parse))
@deprecated(replace="min", version="2.10.2")
def min_ver(ver1, ver2):
"""
Returns the smaller version of two versions strings.
.. deprecated:: 2.10.2
Use Use :func:`min` instead.
:param ver1: version string 1
:param ver2: version string 2
:return: the smaller version of the two
:rtype: :class:`Version`
>>> semver.min_ver("1.0.0", "2.0.0")
'1.0.0'
"""
return str(min(ver1, ver2, key=Version.parse))
@deprecated(replace="str(versionobject)", version="2.10.0")
def format_version(major, minor, patch, prerelease=None, build=None):
"""
Format a version string according to the Semantic Versioning specification.
.. deprecated:: 2.10.0
Use ``str(Version(VERSION)`` instead.
:param int major: the required major part of a version
:param int minor: the required minor part of a version
:param int patch: the required patch part of a version
:param str prerelease: the optional prerelease part of a version
:param str build: the optional build part of a version
:return: the formatted string
:rtype: str
>>> semver.format_version(3, 4, 5, 'pre.2', 'build.4')
'3.4.5-pre.2+build.4'
"""
return str(Version(major, minor, patch, prerelease, build))
@deprecated(version="2.10.0")
def bump_major(version):
"""
Raise the major part of the version string.
.. deprecated:: 2.10.0
Use :meth:`~semver.version.Version.bump_major` instead.
:param: version string
:return: the raised version string
:rtype: str
>>> semver.bump_major("3.4.5")
'4.0.0'
"""
return str(Version.parse(version).bump_major())
@deprecated(version="2.10.0")
def bump_minor(version):
"""
Raise the minor part of the version string.
.. deprecated:: 2.10.0
Use :meth:`~semver.version.Version.bump_minor` instead.
:param: version string
:return: the raised version string
:rtype: str
>>> semver.bump_minor("3.4.5")
'3.5.0'
"""
return str(Version.parse(version).bump_minor())
@deprecated(version="2.10.0")
def bump_patch(version):
"""
Raise the patch part of the version string.
.. deprecated:: 2.10.0
Use :meth:`~semver.version.Version.bump_patch` instead.
:param: version string
:return: the raised version string
:rtype: str
>>> semver.bump_patch("3.4.5")
'3.4.6'
"""
return str(Version.parse(version).bump_patch())
@deprecated(version="2.10.0")
def bump_prerelease(version, token="rc"):
"""
Raise the prerelease part of the version string.
.. deprecated:: 2.10.0
Use :meth:`~semver.version.Version.bump_prerelease` instead.
:param version: version string
:param token: defaults to 'rc'
:return: the raised version string
:rtype: str
>>> semver.bump_prerelease('3.4.5', 'dev')
'3.4.5-dev.1'
"""
return str(Version.parse(version).bump_prerelease(token))
@deprecated(version="2.10.0")
def bump_build(version, token="build"):
"""
Raise the build part of the version string.
.. deprecated:: 2.10.0
Use :meth:`~semver.version.Version.bump_build` instead.
:param version: version string
:param token: defaults to 'build'
:return: the raised version string
:rtype: str
>>> semver.bump_build('3.4.5-rc.1+build.9')
'3.4.5-rc.1+build.10'
"""
return str(Version.parse(version).bump_build(token))
@deprecated(version="2.10.0")
def finalize_version(version):
"""
Remove any prerelease and build metadata from the version string.
.. deprecated:: 2.10.0
Use :meth:`~semver.version.Version.finalize_version` instead.
.. versionadded:: 2.7.9
Added :func:`finalize_version`
:param version: version string
:return: the finalized version string
:rtype: str
>>> semver.finalize_version('1.2.3-rc.5')
'1.2.3'
"""
verinfo = Version.parse(version)
return str(verinfo.finalize_version())
@deprecated(version="2.10.0")
def replace(version, **parts):
"""
Replace one or more parts of a version and return the new string.
.. deprecated:: 2.10.0
Use :meth:`~semver.version.Version.replace` instead.
.. versionadded:: 2.9.0
Added :func:`replace`
:param version: the version string to replace
:param parts: the parts to be updated. Valid keys are:
``major``, ``minor``, ``patch``, ``prerelease``, or ``build``
:return: the replaced version string
:raises TypeError: if ``parts`` contains invalid keys
>>> import semver
>>> semver.replace("1.2.3", major=2, patch=10)
'2.2.10'
"""
return str(Version.parse(version).replace(**parts))
# CLI
cmd_bump = deprecated(cli.cmd_bump, replace="semver.cli.cmd_bump", version="3.0.0")
cmd_check = deprecated(cli.cmd_check, replace="semver.cli.cmd_check", version="3.0.0")
cmd_compare = deprecated(
cli.cmd_compare, replace="semver.cli.cmd_compare", version="3.0.0"
)
cmd_nextver = deprecated(
cli.cmd_nextver, replace="semver.cli.cmd_nextver", version="3.0.0"
)
createparser = deprecated(
cli.createparser, replace="semver.cli.createparser", version="3.0.0"
)
process = deprecated(cli.process, replace="semver.cli.process", version="3.0.0")
main = deprecated(cli.main, replace="semver.cli.main", version="3.0.0")
================================================
FILE: src/semver/_types.py
================================================
"""Typing for semver."""
from functools import partial
from typing import Union, Optional, Tuple, Dict, Iterable, Callable, TypeVar
VersionPart = Union[int, Optional[str]]
VersionTuple = Tuple[int, int, int, Optional[str], Optional[str]]
VersionDict = Dict[str, VersionPart]
VersionIterator = Iterable[VersionPart]
String = Union[str, bytes]
F = TypeVar("F", bound=Callable)
Decorator = Union[Callable[..., F], partial]
================================================
FILE: src/semver/cli.py
================================================
"""
CLI parsing for :command:`pysemver` command.
Each command in :command:`pysemver` is mapped to a ``cmd_`` function.
The :func:`main <semver.cli.main>` function calls
:func:`createparser <semver.cli.createparser>` and
:func:`process <semver.cli.process>` to parse and process
all the commandline options.
The result of each command is printed on stdout.
"""
import argparse
import sys
from typing import cast, List, Optional
from .version import Version
from .__about__ import __version__
def cmd_bump(args: argparse.Namespace) -> str:
"""
Subcommand: Bumps a version.
Synopsis: bump <PART> <VERSION>
<PART> can be major, minor, patch, prerelease, or build
:param args: The parsed arguments
:return: the new, bumped version
"""
maptable = {
"major": "bump_major",
"minor": "bump_minor",
"patch": "bump_patch",
"prerelease": "bump_prerelease",
"build": "bump_build",
}
if args.bump is None:
# When bump is called without arguments,
# print the help and exit
args.parser.parse_args(["bump", "-h"])
ver = Version.parse(args.version)
# get the respective method and call it
func = getattr(ver, maptable[cast(str, args.bump)])
return str(func())
def cmd_check(args: argparse.Namespace) -> None:
"""
Subcommand: Checks if a string is a valid semver version.
Synopsis: check <VERSION>
:param args: The parsed arguments
"""
if Version.is_valid(args.version):
return None
raise ValueError("Invalid version %r" % args.version)
def cmd_compare(args: argparse.Namespace) -> str:
"""
Subcommand: Compare two versions.
Synopsis: compare <VERSION1> <VERSION2>
:param args: The parsed arguments
"""
ver1 = Version.parse(args.version1)
return str(ver1.compare(args.version2))
def cmd_nextver(args: argparse.Namespace) -> str:
"""
Subcommand: Determines the next version, taking prereleases into account.
Synopsis: nextver <VERSION> <PART>
:param args: The parsed arguments
"""
version = Version.parse(args.version)
return str(version.next_version(args.part))
def createparser() -> argparse.ArgumentParser:
"""
Create an :class:`argparse.ArgumentParser` instance.
:return: parser instance
"""
parser = argparse.ArgumentParser(prog=__package__, description=__doc__)
parser.add_argument(
"--version", action="version", version="%(prog)s " + __version__
)
s = parser.add_subparsers()
# create compare subcommand
parser_compare = s.add_parser("compare", help="Compare two versions")
parser_compare.set_defaults(func=cmd_compare)
parser_compare.add_argument("version1", help="First version")
parser_compare.add_argument("version2", help="Second version")
# create bump subcommand
parser_bump = s.add_parser("bump", help="Bumps a version")
parser_bump.set_defaults(func=cmd_bump)
sb = parser_bump.add_subparsers(title="Bump commands", dest="bump")
# Create subparsers for the bump subparser:
for p in (
sb.add_parser("major", help="Bump the major part of the version"),
sb.add_parser("minor", help="Bump the minor part of the version"),
sb.add_parser("patch", help="Bump the patch part of the version"),
sb.add_parser("prerelease", help="Bump the prerelease part of the version"),
sb.add_parser("build", help="Bump the build part of the version"),
):
p.add_argument("version", help="Version to raise")
# Create the check subcommand
parser_check = s.add_parser(
"check", help="Checks if a string is a valid semver version"
)
parser_check.set_defaults(func=cmd_check)
parser_check.add_argument("version", help="Version to check")
# Create the nextver subcommand
parser_nextver = s.add_parser(
"nextver", help="Determines the next version, taking prereleases into account."
)
parser_nextver.set_defaults(func=cmd_nextver)
parser_nextver.add_argument("version", help="Version to raise")
parser_nextver.add_argument(
"part", help="One of 'major', 'minor', 'patch', or 'prerelease'"
)
return parser
def process(args: argparse.Namespace) -> str:
"""
Process the input from the CLI.
:param args: The parsed arguments
:param parser: the parser instance
:return: result of the selected action
"""
if not hasattr(args, "func"):
args.parser.print_help()
raise SystemExit()
# Call the respective function object:
return args.func(args)
def main(cliargs: Optional[List[str]] = None) -> int:
"""
Entry point for the application script.
:param list cliargs: Arguments to parse or None (=use :class:`sys.argv`)
:return: error code
"""
try:
parser = createparser()
args = parser.parse_args(args=cliargs)
# Save parser instance:
args.parser = parser
result = process(args)
if result is not None:
print(result)
return 0
except (ValueError, TypeError) as err:
print("ERROR", err, file=sys.stderr)
return 2
================================================
FILE: src/semver/py.typed
================================================
================================================
FILE: src/semver/version.py
================================================
"""Version handling by a semver compatible version class."""
import re
from functools import wraps
from typing import (
Any,
ClassVar,
Dict,
Iterable,
Optional,
Pattern,
SupportsInt,
Tuple,
Union,
cast,
Callable,
Collection,
Type,
TypeVar,
)
from ._types import (
VersionTuple,
VersionDict,
VersionIterator,
String,
VersionPart,
)
# These types are required here because of circular imports
Comparable = Union["Version", Dict[str, VersionPart], Collection[VersionPart], str]
Comparator = Callable[["Version", Comparable], bool]
T = TypeVar("T", bound="Version")
T_cmp = TypeVar("T_cmp", tuple, str, int)
def _comparator(operator: Comparator) -> Comparator:
"""Wrap a Version binary op method in a type-check."""
@wraps(operator)
def wrapper(self: "Version", other: Comparable) -> bool:
comparable_types = (
type(self),
dict,
tuple,
list,
*String.__args__, # type: ignore
)
if not isinstance(other, comparable_types):
return NotImplemented
return operator(self, other)
return wrapper
def _cmp(a: T_cmp, b: T_cmp) -> int:
"""Return negative if a<b, zero if a==b, positive if a>b."""
return (a > b) - (a < b)
class Version:
"""
A semver compatible version class.
See specification at https://semver.org.
:param major: version when you make incompatible API changes.
:param minor: version when you add functionality in a backwards-compatible manner.
:param patch: version when you make backwards-compatible bug fixes.
:param prerelease: an optional prerelease string
:param build: an optional build string
"""
__slots__ = ("_major", "_minor", "_patch", "_prerelease", "_build")
#: The names of the different parts of a version
NAMES: ClassVar[Tuple[str, ...]] = tuple([item[1:] for item in __slots__])
#: Regex for number in a build
_LAST_NUMBER: ClassVar[Pattern[str]] = re.compile(r"(?:[^\d]*(\d+)[^\d]*)+")
#: Regex for number in a prerelease
_LAST_PRERELEASE: ClassVar[Pattern[str]] = re.compile(r"^(.*\.)?(\d+)$")
#: Regex template for a semver version
_REGEX_TEMPLATE: ClassVar[
str
] = r"""
^
(?P<major>0|[1-9]\d*)
(?:
\.
(?P<minor>0|[1-9]\d*)
(?:
\.
(?P<patch>0|[1-9]\d*)
){opt_patch}
){opt_minor}
(?:-(?P<prerelease>
(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)
(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*
))?
(?:\+(?P<build>
[0-9a-zA-Z-]+
(?:\.[0-9a-zA-Z-]+)*
))?
$
"""
#: Regex for a semver version
_REGEX: ClassVar[Pattern[str]] = re.compile(
_REGEX_TEMPLATE.format(opt_patch="", opt_minor=""),
re.VERBOSE,
)
#: Regex for a semver version that might be shorter
_REGEX_OPTIONAL_MINOR_AND_PATCH: ClassVar[Pattern[str]] = re.compile(
_REGEX_TEMPLATE.format(opt_patch="?", opt_minor="?"),
re.VERBOSE,
)
def __init__(
self,
major: SupportsInt,
minor: SupportsInt = 0,
patch: SupportsInt = 0,
prerelease: Optional[Union[String, int]] = None,
build: Optional[Union[String, int]] = None,
):
# Build a dictionary of the arguments except prerelease and build
version_parts = {"major": int(major), "minor": int(minor), "patch": int(patch)}
for name, value in version_parts.items():
if value < 0:
raise ValueError(
"{!r} is negative. A version can only be positive.".format(name)
)
self._major = version_parts["major"]
self._minor = version_parts["minor"]
self._patch = version_parts["patch"]
self._prerelease = None if prerelease is None else str(prerelease)
self._build = None if build is None else str(build)
@classmethod
def _nat_cmp(cls, a: Optional[str], b: Optional[str]) -> int:
def cmp_prerelease_tag(a, b):
if isinstance(a, int) and isinstance(b, int):
return _cmp(a, b)
elif isinstance(a, int):
return -1
elif isinstance(b, int):
return 1
else:
return _cmp(a, b)
a_parts = [int(x) if x.isdigit() else x for x in (a or "").split(".")]
b_parts = [int(x) if x.isdigit() else x for x in (b or "").split(".")]
for sub_a, sub_b in zip(a_parts, b_parts):
cmp_result = cmp_prerelease_tag(sub_a, sub_b)
if cmp_result != 0:
return cmp_result
return _cmp(len(a_parts), len(b_parts))
@property
def major(self) -> int:
"""The major part of a version (read-only)."""
return self._major
@major.setter
def major(self, value):
raise AttributeError("attribute 'major' is readonly")
@property
def minor(self) -> int:
"""The minor part of a version (read-only)."""
return self._minor
@minor.setter
def minor(self, value):
raise AttributeError("attribute 'minor' is readonly")
@property
def patch(self) -> int:
"""The patch part of a version (read-only)."""
return self._patch
@patch.setter
def patch(self, value):
raise AttributeError("attribute 'patch' is readonly")
@property
def prerelease(self) -> Optional[str]:
"""The prerelease part of a version (read-only)."""
return self._prerelease
@prerelease.setter
def prerelease(self, value):
raise AttributeError("attribute 'prerelease' is readonly")
@property
def build(self) -> Optional[str]:
"""The build part of a version (read-only)."""
return self._build
@build.setter
def build(self, value):
raise AttributeError("attribute 'build' is readonly")
def to_tuple(self) -> VersionTuple:
"""
Convert the Version object to a tuple.
.. versionadded:: 2.10.0
Renamed :meth:`Version._astuple` to :meth:`Version.to_tuple` to
make this function available in the public API.
:return: a tuple with all the parts
>>> semver.Version(5, 3, 1).to_tuple()
(5, 3, 1, None, None)
"""
return (self.major, self.minor, self.patch, self.prerelease, self.build)
def to_dict(self) -> VersionDict:
"""
Convert the Version object to an dict.
.. versionadded:: 2.10.0
Renamed :meth:`Version._asdict` to :meth:`Version.to_dict` to
make this function available in the public API.
:return: an dict with the keys in the order ``major``, ``minor``,
``patch``, ``prerelease``, and ``build``.
>>> semver.Version(3, 2, 1).to_dict()
{'major': 3, 'minor': 2, 'patch': 1, 'prerelease': None, 'build': None}
"""
return dict(
major=self.major,
minor=self.minor,
patch=self.patch,
prerelease=self.prerelease,
build=self.build,
)
def __iter__(self) -> VersionIterator:
"""Return iter(self)."""
yield from self.to_tuple()
@staticmethod
def _increment_prerelease(string: str) -> str:
"""
Check if the last part of a dot-separated string is numeric. If yes,
increase them. Else, add '.0'
:param string: the prerelease version to increment
:return: the incremented string
"""
match = Version._LAST_PRERELEASE.search(string)
if match:
next_ = str(int(match.group(2)) + 1)
string = match.group(1) + next_ if match.group(1) else next_
else:
string += ".0"
return string
@staticmethod
def _increment_string(string: str) -> str:
"""
Look for the last sequence of number(s) in a string and increment.
:param string: the string to search for.
:return: the incremented string
Source:
http://code.activestate.com/recipes/442460-increment-numbers-in-a-string/#c1
"""
match = Version._LAST_NUMBER.search(string)
if match:
next_ = str(int(match.group(1)) + 1)
start, end = match.span(1)
string = string[: max(end - len(next_), start)] + next_ + string[end:]
return string
def bump_major(self) -> "Version":
"""
Raise the major part of the version, return a new object but leave self
untouched.
:return: new object with the raised major part
>>> ver = semver.parse("3.4.5")
>>> ver.bump_major()
Version(major=4, minor=0, patch=0, prerelease=None, build=None)
"""
cls = type(self)
return cls(self._major + 1)
def bump_minor(self) -> "Version":
"""
Raise the minor part of the version, return a new object but leave self
untouched.
:return: new object with the raised minor part
>>> ver = semver.parse("3.4.5")
>>> ver.bump_minor()
Version(major=3, minor=5, patch=0, prerelease=None, build=None)
"""
cls = type(self)
return cls(self._major, self._minor + 1)
def bump_patch(self) -> "Version":
"""
Raise the patch part of the version, return a new object but leave self
untouched.
:return: new object with the raised patch part
>>> ver = semver.parse("3.4.5")
>>> ver.bump_patch()
Version(major=3, minor=4, patch=6, prerelease=None, build=None)
"""
cls = type(self)
return cls(self._major, self._minor, self._patch + 1)
def bump_prerelease(
self,
token: Optional[str] = "rc",
bump_when_empty: Optional[bool] = False
) -> "Version":
"""
Raise the prerelease part of the version, return a new object but leave
self untouched.
.. versionchanged:: 3.1.0
Parameter `bump_when_empty` added. When set to true, bumps the patch version
when called with a version that has no prerelease segment, so the return
value will be considered a newer version.
Adds `.0` to the prerelease if the last part of the dot-separated
prerelease is not a number.
:param token: defaults to ``'rc'``
:return: new :class:`Version` object with the raised prerelease part.
The original object is not modified.
>>> ver = semver.parse("3.4.5")
>>> ver.bump_prerelease().prerelease
'rc.1'
>>> ver.bump_prerelease('').prerelease
'1'
>>> ver.bump_prerelease(None).prerelease
'rc.1'
>>> str(ver.bump_prerelease(bump_when_empty=True))
'3.4.6-rc.1'
"""
cls = type(self)
patch = self._patch
if self._prerelease is not None:
prerelease = cls._increment_prerelease(self._prerelease)
else:
if bump_when_empty:
patch += 1
if token == "":
prerelease = "1"
elif token is None:
prerelease = "rc.1"
else:
prerelease = str(token) + ".1"
return cls(self._major, self._minor, patch, prerelease)
def bump_build(self, token: Optional[str] = "build") -> "Version":
"""
Raise the build part of the version, return a new object but leave self
untouched.
:param token: defaults to ``'build'``
:return: new :class:`Version` object with the raised build part.
The original object is not modified.
>>> ver = semver.parse("3.4.5-rc.1+build.9")
>>> ver.bump_build()
Version(major=3, minor=4, patch=5, prerelease='rc.1', \
build='build.10')
"""
cls = type(self)
if self._build is not None:
build = self._build
elif token == "":
build = "0"
elif token is None:
build = "build.0"
else:
build = str(token) + ".0"
build = cls._increment_string(build)
return cls(self._major, self._minor, self._patch, self._prerelease, build)
def compare(self, other: Comparable) -> int:
"""
Compare self with other.
:param other: the second version
:return: The return value is negative if ver1 < ver2,
zero if ver1 == ver2 and strictly positive if ver1 > ver2
>>> Version.parse("1.0.0").compare("2.0.0")
-1
>>> Version.parse("2.0.0").compare("1.0.0")
1
>>> Version.parse("2.0.0").compare("2.0.0")
0
"""
cls = type(self)
if isinstance(other, String.__args__): # type: ignore
other = cls.parse(other)
elif isinstance(other, dict):
other = cls(**other)
elif isinstance(other, (tuple, list)):
other = cls(*other)
elif not isinstance(other, cls):
raise TypeError(
f"Expected str, bytes, dict, tuple, list, or {cls.__name__} instance, "
f"but got {type(other)}"
)
v1 = self.to_tuple()[:3]
v2 = other.to_tuple()[:3]
x = _cmp(v1, v2)
if x:
return x
rc1, rc2 = self.prerelease, other.prerelease
rccmp = self._nat_cmp(rc1, rc2)
if not rccmp:
return 0
if not rc1:
return 1
elif not rc2:
return -1
return rccmp
def next_version(self, part: str, prerelease_token: str = "rc") -> "Version":
"""
Determines next version, preserving natural order.
.. versionadded:: 2.10.0
This function is taking prereleases into account.
The "major", "minor", and "patch" raises the respective parts like
the ``bump_*`` functions. The real difference is using the
"prerelease" part. It gives you the next patch version of the
prerelease, for example:
>>> str(semver.parse("0.1.4").next_version("prerelease"))
'0.1.5-rc.1'
:param part: One of "major", "minor", "patch", or "prerelease"
:param prerelease_token: prefix string of prerelease, defaults to 'rc'
:return: new object with the appropriate part raised
"""
cls = type(self)
# "build" is currently not used, that's why we use [:-1]
validparts = cls.NAMES[:-1]
if part not in validparts:
raise ValueError(
f"Invalid part. Expected one of {validparts}, but got {part!r}"
)
version = self
if (version.prerelease or version.build) and (
part == "patch"
or (part == "minor" and version.patch == 0)
or (part == "major" and version.minor == version.patch == 0)
):
return version.replace(prerelease=None, build=None)
# Only check the main parts:
if part in cls.NAMES[:3]:
return getattr(version, "bump_" + part)()
else:
return version.bump_prerelease(prerelease_token, bump_when_empty=True)
@_comparator
def __eq__(self, other: Comparable) -> bool: # type: ignore
return self.compare(other) == 0
@_comparator
def __ne__(self, other: Comparable) -> bool: # type: ignore
return self.compare(other) != 0
@_comparator
def __lt__(self, other: Comparable) -> bool:
return self.compare(other) < 0
@_comparator
def __le__(self, other: Comparable) -> bool:
return self.compare(other) <= 0
@_comparator
def __gt__(self, other: Comparable) -> bool:
return self.compare(other) > 0
@_comparator
def __ge__(self, other: Comparable) -> bool:
return self.compare(other) >= 0
def __getitem__(
self, index: Union[int, slice]
) -> Union[int, Optional[str], Tuple[Union[int, str], ...]]:
"""
self.__getitem__(index) <==> self[index] Implement getitem.
If the part requested is undefined, or a part of the range requested
is undefined, it will throw an index error.
Negative indices are not supported.
:param index: a positive integer indicating the
offset or a :func:`slice` object
:raises IndexError: if index is beyond the range or a part is None
:return: the requested part of the version at position index
>>> ver = semver.Version.parse("3.4.5")
>>> ver[0], ver[1], ver[2]
(3, 4, 5)
"""
if isinstance(index, int):
index = slice(index, index + 1)
index = cast(slice, index)
if (
isinstance(index, slice)
and (index.start is not None and index.start < 0)
or (index.stop is not None and index.stop < 0)
):
raise IndexError("Version index cannot be negative")
part = tuple(
filter(lambda p: p is not None, cast(Iterable, self.to_tuple()[index]))
)
if len(part) == 1:
return part[0]
elif not part:
raise IndexError("Version part undefined")
return part
def __repr__(self) -> str:
s = ", ".join("%s=%r" % (key, val) for key, val in self.to_dict().items())
return "%s(%s)" % (type(self).__name__, s)
def __str__(self) -> str:
version = "%d.%d.%d" % (self.major, self.minor, self.patch)
if self.prerelease:
version += "-%s" % self.prerelease
if self.build:
version += "+%s" % self.build
return version
def __hash__(self) -> int:
return hash(self.to_tuple()[:4])
def finalize_version(self) -> "Version":
"""
Remove any prerelease and build metadata from the version.
:return: a new instance with the finalized version string
>>> str(semver.Version.parse('1.2.3-rc.5').finalize_version())
'1.2.3'
"""
cls = type(self)
return cls(self.major, self.minor, self.patch)
def match(self, match_expr: str) -> bool:
"""
Compare self to match a match expression.
:param match_expr: optional operator and version; valid operators are
``<`` smaller than
``>`` greater than
``>=`` greator or equal than
``<=`` smaller or equal than
``==`` equal
``!=`` not equal
:return: True if the expression matches the version, otherwise False
>>> semver.Version.parse("2.0.0").match(">=1.0.0")
True
>>> semver.Version.parse("1.0.0").match(">1.0.0")
False
>>> semver.Version.parse("4.0.4").match("4.0.4")
True
"""
prefix = match_expr[:2]
if prefix in (">=", "<=", "==", "!="):
match_version = match_expr[2:]
elif prefix and prefix[0] in (">", "<"):
prefix = prefix[0]
match_version = match_expr[1:]
elif match_expr and match_expr[0] in "0123456789":
prefix = "=="
match_version = match_expr
else:
raise ValueError(
"match_expr parameter should be in format <op><ver>, "
"where <op> is one of "
"['<', '>', '==', '<=', '>=', '!=']. "
"You provided: %r" % match_expr
)
possibilities_dict = {
">": (1,),
"<": (-1,),
"==": (0,),
"!=": (-1, 1),
">=": (0, 1),
"<=": (-1, 0),
}
possibilities = possibilities_dict[prefix]
cmp_res = self.compare(match_version)
return cmp_res in possibilities
@classmethod
def parse(
cls: Type[T], version: String, optional_minor_and_patch: bool = False
) -> T:
"""
Parse version string to a Version instance.
.. versionchanged:: 2.11.0
Changed method from static to classmethod to
allow subclasses.
.. versionchanged:: 3.0.0
Added optional parameter ``optional_minor_and_patch`` to allow
optional minor and patch parts.
:param version: version string
:param optional_minor_and_patch: if set to true, the version string to parse \
can contain optional minor and patch parts. Optional parts are set to zero.
By default (False), the version string to parse has to follow the semver
specification.
:return: a new :class:`Version` instance
:raises ValueError: if version is invalid
:raises TypeError: if version contains the wrong type
>>> semver.Version.parse('3.4.5-pre.2+build.4')
Version(major=3, minor=4, patch=5, \
prerelease='pre.2', build='build.4')
"""
if isinstanc
gitextract_lbii5c7k/ ├── .coveragerc ├── .editorconfig ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yaml │ │ ├── config.yml │ │ └── feature_request.yaml │ ├── dependabot.yml │ └── workflows/ │ ├── codeql-analysis.yml │ └── python-testing.yml ├── .gitignore ├── .pytest.ini ├── .readthedocs.yaml ├── .ruff.toml ├── CHANGELOG.rst ├── CITATION.cff ├── CONTRIBUTING.rst ├── CONTRIBUTORS ├── LICENSE.txt ├── MANIFEST.in ├── Makefile ├── README.rst ├── SUPPORT.md ├── changelog.d/ │ ├── .gitignore │ ├── 460.bugfix.rst │ ├── 463.trivial.rst │ ├── README.rst │ └── _template.rst ├── docs/ │ ├── Makefile │ ├── _static/ │ │ └── css/ │ │ └── custom.css │ ├── _templates/ │ │ └── layout.html │ ├── advanced/ │ │ ├── coerce.py │ │ ├── combine-pydantic-and-semver.rst │ │ ├── convert-pypi-to-semver.rst │ │ ├── create-subclasses-from-version.rst │ │ ├── deal-with-invalid-versions.rst │ │ ├── display-deprecation-warnings.rst │ │ ├── index.rst │ │ ├── semverwithvprefix.py │ │ └── version-from-file.rst │ ├── api.rst │ ├── build-semver.rst │ ├── changelog-semver3-devel.rst │ ├── changelog.rst │ ├── conf.py │ ├── contribute/ │ │ ├── add-changelog-entry.rst │ │ ├── doc-semver.rst │ │ ├── finish-release.rst │ │ ├── index.rst │ │ ├── prerequisites.rst │ │ ├── release-procedure.rst │ │ ├── report-bugs.rst │ │ └── run-test-suite.rst │ ├── index.rst │ ├── install.rst │ ├── inventories/ │ │ ├── pydantic.inv │ │ └── python-objects.inv │ ├── make.bat │ ├── migration/ │ │ ├── index.rst │ │ ├── migratetosemver3.rst │ │ └── replace-deprecated-functions.rst │ ├── pysemver.rst │ ├── readme.rst │ ├── requirements.txt │ ├── usage/ │ │ ├── access-parts-of-a-version.rst │ │ ├── access-parts-through-index.rst │ │ ├── check-compatible-semver-version.rst │ │ ├── check-valid-semver-version.rst │ │ ├── compare-versions-through-expression.rst │ │ ├── compare-versions.rst │ │ ├── convert-version-into-different-types.rst │ │ ├── create-a-version.rst │ │ ├── determine-version-equality.rst │ │ ├── get-min-and-max-of-multiple-versions.rst │ │ ├── increase-parts-of-a-version_prereleases.rst │ │ ├── index.rst │ │ ├── parse-version-string.rst │ │ ├── raise-parts-of-a-version.rst │ │ ├── replace-parts-of-a-version.rst │ │ ├── semver-version.rst │ │ └── semver_org-version.rst │ └── version-policy.rst ├── pyproject.toml ├── release-procedure.md ├── setup.cfg ├── src/ │ └── semver/ │ ├── __about__.py │ ├── __init__.py │ ├── __main__.py │ ├── _deprecated.py │ ├── _types.py │ ├── cli.py │ ├── py.typed │ └── version.py ├── tests/ │ ├── conftest.py │ ├── test_bump.py │ ├── test_compare.py │ ├── test_deprecated_functions.py │ ├── test_docstrings.py │ ├── test_format.py │ ├── test_immutable.py │ ├── test_index.py │ ├── test_match.py │ ├── test_max-min.py │ ├── test_parsing.py │ ├── test_pysemver-cli.py │ ├── test_replace.py │ ├── test_semver.py │ ├── test_subclass.py │ └── test_typeerror-274.py └── tox.ini
SYMBOL INDEX (185 symbols across 23 files)
FILE: docs/advanced/coerce.py
function coerce (line 20) | def coerce(version: str) -> Tuple[Version, Optional[str]]:
FILE: docs/advanced/semverwithvprefix.py
class SemVerWithVPrefix (line 4) | class SemVerWithVPrefix(Version):
method parse (line 10) | def parse(cls, version: str) -> "SemVerWithVPrefix":
method __str__ (line 25) | def __str__(self) -> str:
FILE: docs/conf.py
function find_version (line 28) | def find_version(path: Path) -> str:
FILE: src/semver/__main__.py
function main (line 20) | def main(cliargs: Optional[List[str]] = None) -> int:
FILE: src/semver/_deprecated.py
function deprecated (line 18) | def deprecated(
function compare (line 91) | def compare(ver1: str, ver2: str) -> int:
function parse (line 117) | def parse(version):
function parse_version_info (line 146) | def parse_version_info(version):
function match (line 174) | def match(version, match_expr):
function max_ver (line 202) | def max_ver(ver1, ver2):
function min_ver (line 221) | def min_ver(ver1, ver2):
function format_version (line 240) | def format_version(major, minor, patch, prerelease=None, build=None):
function bump_major (line 262) | def bump_major(version):
function bump_minor (line 280) | def bump_minor(version):
function bump_patch (line 298) | def bump_patch(version):
function bump_prerelease (line 316) | def bump_prerelease(version, token="rc"):
function bump_build (line 335) | def bump_build(version, token="build"):
function finalize_version (line 354) | def finalize_version(version):
function replace (line 376) | def replace(version, **parts):
FILE: src/semver/cli.py
function cmd_bump (line 21) | def cmd_bump(args: argparse.Namespace) -> str:
function cmd_check (line 49) | def cmd_check(args: argparse.Namespace) -> None:
function cmd_compare (line 62) | def cmd_compare(args: argparse.Namespace) -> str:
function cmd_nextver (line 74) | def cmd_nextver(args: argparse.Namespace) -> str:
function createparser (line 86) | def createparser() -> argparse.ArgumentParser:
function process (line 139) | def process(args: argparse.Namespace) -> str:
function main (line 155) | def main(cliargs: Optional[List[str]] = None) -> int:
FILE: src/semver/version.py
function _comparator (line 38) | def _comparator(operator: Comparator) -> Comparator:
function _cmp (line 57) | def _cmp(a: T_cmp, b: T_cmp) -> int:
class Version (line 62) | class Version:
method __init__ (line 119) | def __init__(
method _nat_cmp (line 143) | def _nat_cmp(cls, a: Optional[str], b: Optional[str]) -> int:
method major (line 165) | def major(self) -> int:
method major (line 170) | def major(self, value):
method minor (line 174) | def minor(self) -> int:
method minor (line 179) | def minor(self, value):
method patch (line 183) | def patch(self) -> int:
method patch (line 188) | def patch(self, value):
method prerelease (line 192) | def prerelease(self) -> Optional[str]:
method prerelease (line 197) | def prerelease(self, value):
method build (line 201) | def build(self) -> Optional[str]:
method build (line 206) | def build(self, value):
method to_tuple (line 209) | def to_tuple(self) -> VersionTuple:
method to_dict (line 224) | def to_dict(self) -> VersionDict:
method __iter__ (line 246) | def __iter__(self) -> VersionIterator:
method _increment_prerelease (line 251) | def _increment_prerelease(string: str) -> str:
method _increment_string (line 268) | def _increment_string(string: str) -> str:
method bump_major (line 285) | def bump_major(self) -> "Version":
method bump_minor (line 299) | def bump_minor(self) -> "Version":
method bump_patch (line 313) | def bump_patch(self) -> "Version":
method bump_prerelease (line 327) | def bump_prerelease(
method bump_build (line 374) | def bump_build(self, token: Optional[str] = "build") -> "Version":
method compare (line 401) | def compare(self, other: Comparable) -> int:
method next_version (line 447) | def next_version(self, part: str, prerelease_token: str = "rc") -> "Ve...
method __eq__ (line 488) | def __eq__(self, other: Comparable) -> bool: # type: ignore
method __ne__ (line 492) | def __ne__(self, other: Comparable) -> bool: # type: ignore
method __lt__ (line 496) | def __lt__(self, other: Comparable) -> bool:
method __le__ (line 500) | def __le__(self, other: Comparable) -> bool:
method __gt__ (line 504) | def __gt__(self, other: Comparable) -> bool:
method __ge__ (line 508) | def __ge__(self, other: Comparable) -> bool:
method __getitem__ (line 511) | def __getitem__(
method __repr__ (line 551) | def __repr__(self) -> str:
method __str__ (line 555) | def __str__(self) -> str:
method __hash__ (line 563) | def __hash__(self) -> int:
method finalize_version (line 566) | def finalize_version(self) -> "Version":
method match (line 578) | def match(self, match_expr: str) -> bool:
method parse (line 630) | def parse(
method replace (line 676) | def replace(self, **parts: Union[int, Optional[str]]) -> "Version":
method is_valid (line 703) | def is_valid(cls, version: str) -> bool:
method is_compatible (line 722) | def is_compatible(self, other: "Version") -> bool:
FILE: tests/conftest.py
function add_semver (line 13) | def add_semver(doctest_namespace):
function version (line 22) | def version():
FILE: tests/test_bump.py
function test_should_bump_major (line 14) | def test_should_bump_major():
function test_should_bump_minor (line 18) | def test_should_bump_minor():
function test_should_bump_patch (line 22) | def test_should_bump_patch():
function test_should_versioninfo_bump_major_and_minor (line 26) | def test_should_versioninfo_bump_major_and_minor():
function test_should_versioninfo_bump_minor_and_patch (line 32) | def test_should_versioninfo_bump_minor_and_patch():
function test_should_versioninfo_bump_patch_and_prerelease (line 39) | def test_should_versioninfo_bump_patch_and_prerelease():
function test_should_versioninfo_bump_patch_and_prerelease_with_token (line 46) | def test_should_versioninfo_bump_patch_and_prerelease_with_token():
function test_should_versioninfo_bump_prerelease_and_build (line 53) | def test_should_versioninfo_bump_prerelease_and_build():
function test_should_versioninfo_bump_prerelease_and_build_with_token (line 60) | def test_should_versioninfo_bump_prerelease_and_build_with_token():
function test_should_versioninfo_bump_multiple (line 67) | def test_should_versioninfo_bump_multiple():
function test_should_versioninfo_bump_prerelease_with_empty_str (line 77) | def test_should_versioninfo_bump_prerelease_with_empty_str():
function test_should_versioninfo_bump_prerelease_with_none (line 84) | def test_should_versioninfo_bump_prerelease_with_none():
function test_should_versioninfo_bump_prerelease_nonnumeric (line 91) | def test_should_versioninfo_bump_prerelease_nonnumeric():
function test_should_versioninfo_bump_prerelease_nonnumeric_nine (line 98) | def test_should_versioninfo_bump_prerelease_nonnumeric_nine():
function test_should_versioninfo_bump_prerelease_bump_patch (line 105) | def test_should_versioninfo_bump_prerelease_bump_patch():
function test_should_versioninfo_bump_patch_and_prerelease_bump_patch (line 112) | def test_should_versioninfo_bump_patch_and_prerelease_bump_patch():
function test_should_versioninfo_bump_build_with_empty_str (line 119) | def test_should_versioninfo_bump_build_with_empty_str():
function test_should_versioninfo_bump_build_with_none (line 126) | def test_should_versioninfo_bump_build_with_none():
function test_should_ignore_extensions_for_bump (line 133) | def test_should_ignore_extensions_for_bump():
function test_should_bump_prerelease (line 146) | def test_should_bump_prerelease(version, token, expected, expected_compa...
function test_should_ignore_build_on_prerelease_bump (line 151) | def test_should_ignore_build_on_prerelease_bump():
function test_should_bump_build (line 164) | def test_should_bump_build(version, expected):
FILE: tests/test_compare.py
function test_should_get_less (line 20) | def test_should_get_less(left, right):
function test_should_get_greater (line 37) | def test_should_get_greater(left, right):
function test_should_raise_value_error_for_invalid_value (line 44) | def test_should_raise_value_error_for_invalid_value(left, right):
function test_should_follow_specification_comparison (line 49) | def test_should_follow_specification_comparison():
function test_should_compare_rc_builds (line 77) | def test_should_compare_rc_builds(left, right):
function test_should_compare_release_candidate_with_release (line 84) | def test_should_compare_release_candidate_with_release(left, right):
function test_should_say_equal_versions_are_equal (line 97) | def test_should_say_equal_versions_are_equal(left, right):
function test_should_compare_versions_with_build_and_release (line 105) | def test_should_compare_versions_with_build_and_release(left, right, exp...
function test_should_ignore_builds_on_compare (line 118) | def test_should_ignore_builds_on_compare(left, right, expected):
function test_should_get_more_rc1 (line 122) | def test_should_get_more_rc1():
function test_should_compare_prerelease_with_numbers_and_letters (line 126) | def test_should_compare_prerelease_with_numbers_and_letters():
function test_should_compare_version_info_objects (line 133) | def test_should_compare_version_info_objects():
function test_should_compare_version_dictionaries (line 163) | def test_should_compare_version_dictionaries():
function test_should_compare_version_tuples (line 202) | def test_should_compare_version_tuples(t):
function test_should_compare_version_list (line 231) | def test_should_compare_version_list(lst):
function test_should_compare_version_string (line 260) | def test_should_compare_version_string(s):
function test_should_not_allow_to_compare_invalid_versionstring (line 280) | def test_should_not_allow_to_compare_invalid_versionstring(s):
function test_should_not_allow_to_compare_version_with_int (line 288) | def test_should_not_allow_to_compare_version_with_int():
function test_should_compare_prerelease_and_build_with_numbers (line 298) | def test_should_compare_prerelease_and_build_with_numbers():
FILE: tests/test_deprecated_functions.py
function test_should_raise_deprecation_warnings (line 59) | def test_should_raise_deprecation_warnings(func, args, kwargs):
function test_deprecated_deco_without_argument (line 69) | def test_deprecated_deco_without_argument():
FILE: tests/test_docstrings.py
function getallfunctions (line 8) | def getallfunctions(module=semver):
function test_fordocstrings (line 35) | def test_fordocstrings(func):
FILE: tests/test_format.py
function test_should_finalize_version (line 17) | def test_should_finalize_version(version, expected):
function test_should_correctly_format_version (line 21) | def test_should_correctly_format_version():
function test_parse_method_for_version_info (line 29) | def test_parse_method_for_version_info():
function test_repr (line 64) | def test_repr(version, expected):
FILE: tests/test_immutable.py
function test_immutable_major (line 4) | def test_immutable_major(version):
function test_immutable_minor (line 9) | def test_immutable_minor(version):
function test_immutable_patch (line 14) | def test_immutable_patch(version):
function test_immutable_prerelease (line 19) | def test_immutable_prerelease(version):
function test_immutable_build (line 24) | def test_immutable_build(version):
function test_immutable_unknown_attribute (line 29) | def test_immutable_unknown_attribute(version):
FILE: tests/test_index.py
function test_version_info_should_be_accessed_with_index (line 26) | def test_version_info_should_be_accessed_with_index(version, index, expe...
function test_version_info_should_be_accessed_with_slice_object (line 54) | def test_version_info_should_be_accessed_with_slice_object(
function test_version_info_should_throw_index_error (line 76) | def test_version_info_should_throw_index_error(version, index):
function test_version_info_should_throw_index_error_when_negative_index (line 92) | def test_version_info_should_throw_index_error_when_negative_index(versi...
FILE: tests/test_match.py
function test_should_match_simple (line 6) | def test_should_match_simple():
function test_should_no_match_simple (line 10) | def test_should_no_match_simple():
function test_should_match_not_equal (line 22) | def test_should_match_not_equal(left, right, expected):
function test_should_match_equal_by_default (line 34) | def test_should_match_equal_by_default(left, right, expected):
function test_should_not_raise_value_error_for_expected_match_expression (line 49) | def test_should_not_raise_value_error_for_expected_match_expression(
function test_should_raise_value_error_for_unexpected_match_expression (line 58) | def test_should_raise_value_error_for_unexpected_match_expression(left, ...
function test_should_raise_value_error_for_invalid_match_expression (line 64) | def test_should_raise_value_error_for_invalid_match_expression(left, rig...
FILE: tests/test_max-min.py
function test_should_get_max (line 6) | def test_should_get_max():
function test_should_get_max_same (line 10) | def test_should_get_max_same():
function test_should_get_min (line 14) | def test_should_get_min():
function test_should_get_min_same (line 18) | def test_should_get_min_same():
function test_prerelease_order (line 42) | def test_prerelease_order(left, right, expected):
FILE: tests/test_parsing.py
function test_should_parse_version (line 51) | def test_should_parse_version(version, expected):
function test_should_parse_version_with_optional_minor_and_patch (line 101) | def test_should_parse_version_with_optional_minor_and_patch(version, exp...
function test_parse_version_info_str_hash (line 106) | def test_parse_version_info_str_hash():
function test_should_parse_zero_prerelease (line 141) | def test_should_parse_zero_prerelease(version, expected):
function test_should_raise_value_error_for_zero_prefixed_versions (line 147) | def test_should_raise_value_error_for_zero_prefixed_versions(version):
function test_equal_versions_have_equal_hashes (line 152) | def test_equal_versions_have_equal_hashes():
function test_parse_method_for_version_info (line 166) | def test_parse_method_for_version_info():
function test_next_version_with_invalid_parts (line 172) | def test_next_version_with_invalid_parts():
function test_next_version_with_versioninfo (line 203) | def test_next_version_with_versioninfo(version, part, expected):
FILE: tests/test_pysemver-cli.py
function does_not_raise (line 19) | def does_not_raise(item):
function test_should_parse_cli_arguments (line 40) | def test_should_parse_cli_arguments(cli, expected):
function test_should_process_parsed_cli_arguments (line 102) | def test_should_process_parsed_cli_arguments(func, args, expectation):
function test_should_process_print (line 108) | def test_should_process_print(capsys):
function test_should_process_raise_error (line 115) | def test_should_process_raise_error(capsys):
function test_should_raise_systemexit_when_called_with_empty_arguments (line 122) | def test_should_raise_systemexit_when_called_with_empty_arguments():
function test_should_raise_systemexit_when_bump_iscalled_with_empty_arguments (line 127) | def test_should_raise_systemexit_when_bump_iscalled_with_empty_arguments():
function test_should_process_check_iscalled_with_valid_version (line 132) | def test_should_process_check_iscalled_with_valid_version(capsys):
function test_main_file_should_call_cli_main (line 140) | def test_main_file_should_call_cli_main(package_name):
FILE: tests/test_replace.py
function test_replace_method_replaces_requested_parts (line 25) | def test_replace_method_replaces_requested_parts(version, parts, expected):
function test_replace_raises_TypeError_for_invalid_keyword_arg (line 29) | def test_replace_raises_TypeError_for_invalid_keyword_arg():
function test_should_return_versioninfo_with_replaced_parts (line 44) | def test_should_return_versioninfo_with_replaced_parts(version, parts, e...
function test_replace_raises_ValueError_for_non_numeric_values (line 48) | def test_replace_raises_ValueError_for_non_numeric_values():
FILE: tests/test_semver.py
function test_should_private_increment_string (line 9) | def test_should_private_increment_string(string, expected):
function test_should_not_allow_negative_numbers (line 22) | def test_should_not_allow_negative_numbers(ver):
function test_should_versioninfo_to_dict (line 27) | def test_should_versioninfo_to_dict(version):
function test_should_versioninfo_to_tuple (line 33) | def test_should_versioninfo_to_tuple(version):
function test_version_info_should_be_iterable (line 39) | def test_version_info_should_be_iterable(version):
function test_should_be_able_to_use_strings_as_major_minor_patch (line 49) | def test_should_be_able_to_use_strings_as_major_minor_patch():
function test_using_non_numeric_string_as_major_minor_patch_throws (line 59) | def test_using_non_numeric_string_as_major_minor_patch_throws():
function test_should_be_able_to_use_integers_as_prerelease_build (line 68) | def test_should_be_able_to_use_integers_as_prerelease_build():
function test_should_versioninfo_isvalid (line 75) | def test_should_versioninfo_isvalid():
function test_versioninfo_compare_should_raise_when_passed_invalid_value (line 80) | def test_versioninfo_compare_should_raise_when_passed_invalid_value():
function test_should_succeed_compatible_match (line 95) | def test_should_succeed_compatible_match(old, new):
function test_should_fail_compatible_match (line 113) | def test_should_fail_compatible_match(old, new):
function test_should_fail_with_incompatible_type_for_compatible_match (line 127) | def test_should_fail_with_incompatible_type_for_compatible_match(wrongty...
function test_should_succeed_with_compatible_subclass_for_is_compatible (line 133) | def test_should_succeed_with_compatible_subclass_for_is_compatible():
FILE: tests/test_subclass.py
function test_subclass_from_versioninfo (line 5) | def test_subclass_from_versioninfo():
function test_replace_from_subclass (line 23) | def test_replace_from_subclass():
function test_compare_with_subclass (line 57) | def test_compare_with_subclass():
FILE: tests/test_typeerror-274.py
function test_should_work_with_string_and_bytes (line 5) | def test_should_work_with_string_and_bytes():
function test_should_not_work_with_invalid_args (line 12) | def test_should_not_work_with_invalid_args():
Condensed preview — 108 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (256K chars).
[
{
"path": ".coveragerc",
"chars": 229,
"preview": "# https://pytest-cov.readthedocs.io/\n\n[run]\nsource = semver\nbranch = True\n\n[report]\nshow_missing = true\nprecision = 1\nex"
},
{
"path": ".editorconfig",
"chars": 238,
"preview": "# http://editorconfig.org\nroot = true\n\n[*]\nend_of_line = lf\ncharset = utf-8\nmax_line_length = 99\n\n[*.py]\nindent_style = "
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yaml",
"chars": 2305,
"preview": "name: 🐞Bug report\ndescription: Create a bug report to help us improve\n# title: ''\nlabels: [bug]\nassignees: [tomschr]\n\nbo"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 325,
"preview": "blank_issues_enabled: true\ncontact_links:\n - name: Community Support\n url: https://github.com/python-semver/python-s"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.yaml",
"chars": 1025,
"preview": "name: Feature request\ndescription: Suggest an idea for this project\n# title: ''\nlabels: [enhancement]\nassignees: [tomsch"
},
{
"path": ".github/dependabot.yml",
"chars": 691,
"preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
},
{
"path": ".github/workflows/codeql-analysis.yml",
"chars": 2363,
"preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
},
{
"path": ".github/workflows/python-testing.yml",
"chars": 3390,
"preview": "---\nname: Python\n\n# HINT: Sync this paths with the egrep in step check_files\non:\n push:\n branches: [ \"master\", \"main"
},
{
"path": ".gitignore",
"chars": 4586,
"preview": "# Python .gitignore file from gh://github/gitignore/Python.gitignore\n#\n# Byte-compiled / optimized / DLL files\n__pycache"
},
{
"path": ".pytest.ini",
"chars": 428,
"preview": "[pytest]\nnorecursedirs = .git build .env/ env/ .pyenv/ .tmp/ .eggs/ venv/\ntestpaths = tests docs\npythonpath = src tests\n"
},
{
"path": ".readthedocs.yaml",
"chars": 658,
"preview": "# .readthedocs.yaml\n# Read the Docs configuration file\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html f"
},
{
"path": ".ruff.toml",
"chars": 1165,
"preview": "#\n# Configuration file for ruff\n# See https://docs.astral.sh/ruff/configuration/\n\nline-length = 88\nindent-width = 4\nincl"
},
{
"path": "CHANGELOG.rst",
"chars": 14568,
"preview": "##########\nChange Log\n##########\n\nChanges for the upcoming release can be found in\nthe `\"changelog.d\" directory <https:/"
},
{
"path": "CITATION.cff",
"chars": 2311,
"preview": "# This CITATION.cff file was generated with cffinit:\n# https://bit.ly/cffinit\n\ncff-version: 1.2.0\ntitle: python-semver\nm"
},
{
"path": "CONTRIBUTING.rst",
"chars": 1891,
"preview": ".. _contributing:\n\nContributing to semver\n======================\n\nThe semver source code is managed using Git and is hos"
},
{
"path": "CONTRIBUTORS",
"chars": 1746,
"preview": "############\nContributors\n############\n\nPython Semver library\n#####################\n\nThis document records the primary m"
},
{
"path": "LICENSE.txt",
"chars": 1499,
"preview": "Copyright (c) 2013, Konstantine Rybnikov\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with o"
},
{
"path": "MANIFEST.in",
"chars": 434,
"preview": "include *.md\ninclude *.rst\ninclude *.txt\ninclude CONTRIBUTORS\ninclude CITATION.cff\ninclude Makefile\ninclude changelog.d/"
},
{
"path": "Makefile",
"chars": 69,
"preview": "sdist:\n\tpython setup.py sdist bdist_wheel --universal\n\n.PHONY sdist\n\n"
},
{
"path": "README.rst",
"chars": 3585,
"preview": "Quickstart\n==========\n\n.. teaser-begin\n\nA Python module to simplify `semantic versioning`_.\n\n|GHAction| |python-support|"
},
{
"path": "SUPPORT.md",
"chars": 601,
"preview": "# Getting support\n\nIf you need help, try these ways:\n\n* Ask your questions on the [discussion](https://github.com/python"
},
{
"path": "changelog.d/.gitignore",
"chars": 12,
"preview": "!.gitignore\n"
},
{
"path": "changelog.d/460.bugfix.rst",
"chars": 510,
"preview": ":meth:`~semver.version.Version.bump_prerelease` will now add `.0` to an\nexisting prerelease when the last segment of the"
},
{
"path": "changelog.d/463.trivial.rst",
"chars": 48,
"preview": "Remove double code in :meth:`Version.bump_build`"
},
{
"path": "changelog.d/README.rst",
"chars": 2501,
"preview": "The ``changelog.d`` Directory\n=============================\n\n.. This file is also included into the documentation\n\n.. -t"
},
{
"path": "changelog.d/_template.rst",
"chars": 964,
"preview": "{% for section, _ in sections.items() %}\n{% set underline = underlines[0] %}{% if section %}{{section}}\n{{ underline * s"
},
{
"path": "docs/Makefile",
"chars": 761,
"preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS =\nSPHI"
},
{
"path": "docs/_static/css/custom.css",
"chars": 1234,
"preview": "/*\nhttps://github.com/bitprophet/alabaster\n*/\n\n/* Roboto (Sans), Roboto Slab (\"serif\"), Roboto Mono*/\n@import url('https"
},
{
"path": "docs/_templates/layout.html",
"chars": 162,
"preview": "{#\n Import the theme's layout.\n#}\n{% extends \"!layout.html\" %}\n\n\n{%- block extrahead %}\n {{- super() }}\n <meta name=\""
},
{
"path": "docs/advanced/coerce.py",
"chars": 1208,
"preview": "import re\nfrom semver import Version\nfrom typing import Optional, Tuple\n\n\nBASEVERSION = re.compile(\n r\"\"\"[vV]?\n "
},
{
"path": "docs/advanced/combine-pydantic-and-semver.rst",
"chars": 3405,
"preview": "Combining Pydantic and semver\n=============================\n\n.. meta::\n :description lang=en:\n Combining Pydantic"
},
{
"path": "docs/advanced/convert-pypi-to-semver.rst",
"chars": 6271,
"preview": "Converting Versions between PyPI and semver\n===========================================\n\n.. meta::\n :description lang="
},
{
"path": "docs/advanced/create-subclasses-from-version.rst",
"chars": 1173,
"preview": ".. _sec_creating_subclasses_from_versioninfo:\n\nCreating Subclasses from Version\n================================\n\n.. met"
},
{
"path": "docs/advanced/deal-with-invalid-versions.rst",
"chars": 1203,
"preview": ".. _sec_dealing_with_invalid_versions:\n\nDealing with Invalid Versions\n=============================\n\n.. meta::\n :descr"
},
{
"path": "docs/advanced/display-deprecation-warnings.rst",
"chars": 1190,
"preview": ".. _sec_display_deprecation_warnings:\n\nDisplaying Deprecation Warnings\n===============================\n\n.. meta::\n :de"
},
{
"path": "docs/advanced/index.rst",
"chars": 320,
"preview": "Advanced topics\n===============\n\n.. meta::\n :description lang=en:\n Advanced topics for Python semver\n\n.. toctree:"
},
{
"path": "docs/advanced/semverwithvprefix.py",
"chars": 819,
"preview": "from semver import Version\n\n\nclass SemVerWithVPrefix(Version):\n \"\"\"\n A subclass of Version which allows a \"v\" pref"
},
{
"path": "docs/advanced/version-from-file.rst",
"chars": 788,
"preview": ".. _sec_reading_versions_from_file:\n\nReading Versions from File\n==========================\n\n.. meta::\n :description la"
},
{
"path": "docs/api.rst",
"chars": 1878,
"preview": ".. _api:\n\nAPI Reference\n=============\n\n.. meta::\n :description lang=en:\n API reference about Python semver\n\n.. cu"
},
{
"path": "docs/build-semver.rst",
"chars": 772,
"preview": ".. _build-semver:\n\nBuilding semver\n===============\n\n.. meta::\n :description lang=en:\n Building semver\n\n.. _Instal"
},
{
"path": "docs/changelog-semver3-devel.rst",
"chars": 10562,
"preview": "\n#############################\nChangelog semver3 development\n#############################\n\nThis site contains all the c"
},
{
"path": "docs/changelog.rst",
"chars": 47,
"preview": ".. _change-log:\n\n.. include:: ../CHANGELOG.rst\n"
},
{
"path": "docs/conf.py",
"chars": 8851,
"preview": "#!/usr/bin/env python3\n#\n# python-semver documentation build configuration file\n#\n# This file is execfile()d with the cu"
},
{
"path": "docs/contribute/add-changelog-entry.rst",
"chars": 225,
"preview": ".. _add-changelog:\n\nAdding a Changelog Entry\n========================\n\n.. meta::\n :description lang=en:\n Adding a"
},
{
"path": "docs/contribute/doc-semver.rst",
"chars": 2879,
"preview": ".. _doc:\n\nDocumenting semver\n==================\n\n.. meta::\n :description lang=en:\n Documenting semver with type a"
},
{
"path": "docs/contribute/finish-release.rst",
"chars": 659,
"preview": ".. _finish-release:\n\nFinish the Release\n==================\n\n.. meta::\n :description lang=en:\n Finish the semver r"
},
{
"path": "docs/contribute/index.rst",
"chars": 597,
"preview": ".. _contributing:\n\nContributing to semver\n======================\n\n.. meta::\n :description lang=en:\n Contributing "
},
{
"path": "docs/contribute/prerequisites.rst",
"chars": 517,
"preview": "Prerequisites\n-------------\n\n.. meta::\n :description lang=en:\n Overview of prerequisites for contributing\n\n.. -te"
},
{
"path": "docs/contribute/release-procedure.rst",
"chars": 3319,
"preview": "Release Procedure\n=================\n\n.. meta::\n :description lang=en:\n Release procedure: prepare and create the "
},
{
"path": "docs/contribute/report-bugs.rst",
"chars": 668,
"preview": ".. _report-bugs:\n\nReporting Bugs and Asking Questions\n-----------------------------------\n\n.. meta::\n :description lan"
},
{
"path": "docs/contribute/run-test-suite.rst",
"chars": 2011,
"preview": ".. _testsuite:\n\nRunning the Test Suite\n======================\n\n.. meta::\n :description lang=en:\n Running the test"
},
{
"path": "docs/index.rst",
"chars": 656,
"preview": "Semver |version| -- Semantic Versioning\n=======================================\n\n.. meta::\n :description lang=en:\n "
},
{
"path": "docs/install.rst",
"chars": 2864,
"preview": "Installing semver\n=================\n\n.. meta::\n :description lang=en:\n Installing semver on the system\n\nRelease P"
},
{
"path": "docs/make.bat",
"chars": 804,
"preview": "@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=py"
},
{
"path": "docs/migration/index.rst",
"chars": 219,
"preview": "Migrating to semver3\n====================\n\n.. meta::\n :description lang=en:\n Migrating from semver version 2 to v"
},
{
"path": "docs/migration/migratetosemver3.rst",
"chars": 1648,
"preview": ".. _semver2-to-3:\n\nMigrating from semver2 to semver3\n=================================\n\n.. meta::\n :description lang=e"
},
{
"path": "docs/migration/replace-deprecated-functions.rst",
"chars": 4263,
"preview": ".. _sec_replace_deprecated_functions:\n\nReplacing Deprecated Functions\n==============================\n\n.. meta::\n :desc"
},
{
"path": "docs/pysemver.rst",
"chars": 3842,
"preview": ":orphan:\n\npysemver |version|\n==================\n\n.. meta::\n :description lang=en:\n Commandline tool pysemver desc"
},
{
"path": "docs/readme.rst",
"chars": 120,
"preview": "If you are searching for how to stay compatible\nwith semver3, refer to :ref:`semver2-to-3`.\n\n.. include:: ../README.rst\n"
},
{
"path": "docs/requirements.txt",
"chars": 147,
"preview": "# Sphinx requirements file for documentation\n# Required in .readthedocs.yaml\nsphinx==7.0.1\nsphinx-argparse==0.4.0\nsphinx"
},
{
"path": "docs/usage/access-parts-of-a-version.rst",
"chars": 1197,
"preview": ".. _sec.properties.parts:\n\nAccessing Parts of a Version Through Names\n==========================================\n\n.. met"
},
{
"path": "docs/usage/access-parts-through-index.rst",
"chars": 1323,
"preview": ".. _sec.getitem.parts:\n\nAccessing Parts Through Index Numbers\n=====================================\n\n.. meta::\n :descr"
},
{
"path": "docs/usage/check-compatible-semver-version.rst",
"chars": 2304,
"preview": "Checking for a Compatible Semver Version\n========================================\n\n.. meta::\n :description lang=en:\n "
},
{
"path": "docs/usage/check-valid-semver-version.rst",
"chars": 390,
"preview": "Checking for a Valid Semver Version\n===================================\n\n.. meta::\n :description lang=en:\n Checki"
},
{
"path": "docs/usage/compare-versions-through-expression.rst",
"chars": 1209,
"preview": "Comparing Versions through an Expression\n========================================\n\n.. meta::\n :description lang=en:\n "
},
{
"path": "docs/usage/compare-versions.rst",
"chars": 2593,
"preview": "Comparing Versions\n==================\n\n.. meta::\n :description lang=en:\n Comparing versions with semver.compare a"
},
{
"path": "docs/usage/convert-version-into-different-types.rst",
"chars": 954,
"preview": ".. _sec.convert.versions:\n\nConverting a Version instance into Different Types\n=========================================="
},
{
"path": "docs/usage/create-a-version.rst",
"chars": 3604,
"preview": "Creating a Version\n==================\n\n.. meta::\n :description lang=en:\n Creating a version using different metho"
},
{
"path": "docs/usage/determine-version-equality.rst",
"chars": 707,
"preview": "Determining Version Equality\n============================\n\n.. meta::\n :description lang=en:\n Determining verison "
},
{
"path": "docs/usage/get-min-and-max-of-multiple-versions.rst",
"chars": 1760,
"preview": ".. _sec_max_min:\n\nGetting Minimum and Maximum of Multiple Versions\n================================================\n\n.. "
},
{
"path": "docs/usage/increase-parts-of-a-version_prereleases.rst",
"chars": 787,
"preview": ".. _increase-parts-of-a-version:\n\nIncreasing Parts of a Version Taking into Account Prereleases\n========================"
},
{
"path": "docs/usage/index.rst",
"chars": 519,
"preview": "Using semver\n============\n\n.. toctree::\n\n semver_org-version\n semver-version\n create-a-version\n parse-version-st"
},
{
"path": "docs/usage/parse-version-string.rst",
"chars": 651,
"preview": "Parsing a Version String\n========================\n\n\"Parsing\" in this context means to identify the different parts in a "
},
{
"path": "docs/usage/raise-parts-of-a-version.rst",
"chars": 2939,
"preview": "Raising Parts of a Version\n==========================\n\n.. note::\n\n Keep in mind, by default, \"raising\" the pre-release"
},
{
"path": "docs/usage/replace-parts-of-a-version.rst",
"chars": 672,
"preview": ".. _sec.replace.parts:\n\nReplacing Parts of a Version\n============================\n\nIf you want to replace different part"
},
{
"path": "docs/usage/semver-version.rst",
"chars": 167,
"preview": "Getting the Version of semver\n=============================\n\nTo know the version of semver itself, use the following con"
},
{
"path": "docs/usage/semver_org-version.rst",
"chars": 330,
"preview": "Getting the Implemented semver.org Version\n==========================================\n\nThe semver.org page is the author"
},
{
"path": "docs/version-policy.rst",
"chars": 1687,
"preview": ".. _version-policy:\n\nVersion Policy\n==============\n\n.. |MAINT| replace:: ``maint/v2``\n.. _MAINT: https://github.com/pyth"
},
{
"path": "pyproject.toml",
"chars": 4454,
"preview": "#\n#\n# See also https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html\n#\n# General idea taken from\n# https:"
},
{
"path": "release-procedure.md",
"chars": 4622,
"preview": "# Release Procedure\n\nThe following procedures gives a short overview of what steps are needed to\ncreate a new release.\n\n"
},
{
"path": "setup.cfg",
"chars": 296,
"preview": "#\n# Metadata for setup.py\n#\n# See https://setuptools.rtfd.io/en/latest/userguide/declarative_config.html\n\n\n[pycodestyle]"
},
{
"path": "src/semver/__about__.py",
"chars": 815,
"preview": "\"\"\"\nMetadata about semver.\n\nContains information about semver's version, the implemented version\nof the semver specifict"
},
{
"path": "src/semver/__init__.py",
"chars": 1269,
"preview": "\"\"\"\nSemver package major release 3.\n\nA Python module for semantic versioning. Simplifies comparing versions.\n\"\"\"\n\nfrom ."
},
{
"path": "src/semver/__main__.py",
"chars": 593,
"preview": "\"\"\"\nModule to support call with :file:`__main__.py`. Used to support the following\ncall::\n\n $ python3 -m semver ...\n\n"
},
{
"path": "src/semver/_deprecated.py",
"chars": 11273,
"preview": "\"\"\"\nContains all deprecated functions.\n\n.. autofunction: deprecated\n\"\"\"\n\nimport inspect\nimport warnings\nfrom functools i"
},
{
"path": "src/semver/_types.py",
"chars": 422,
"preview": "\"\"\"Typing for semver.\"\"\"\n\nfrom functools import partial\nfrom typing import Union, Optional, Tuple, Dict, Iterable, Calla"
},
{
"path": "src/semver/cli.py",
"chars": 5189,
"preview": "\"\"\"\nCLI parsing for :command:`pysemver` command.\n\nEach command in :command:`pysemver` is mapped to a ``cmd_`` function.\n"
},
{
"path": "src/semver/py.typed",
"chars": 0,
"preview": ""
},
{
"path": "src/semver/version.py",
"chars": 24923,
"preview": "\"\"\"Version handling by a semver compatible version class.\"\"\"\n\nimport re\nfrom functools import wraps\nfrom typing import ("
},
{
"path": "tests/conftest.py",
"chars": 739,
"preview": "import sys\n\nimport pytest\n\nimport semver\n\nfrom coerce import coerce # noqa:E402\nfrom semverwithvprefix import SemVerWit"
},
{
"path": "tests/test_bump.py",
"chars": 5212,
"preview": "import pytest\n\nfrom semver import (\n bump_build,\n bump_major,\n bump_minor,\n bump_patch,\n bump_prerelease,"
},
{
"path": "tests/test_compare.py",
"chars": 8068,
"preview": "import pytest\n\nimport semver\nfrom semver import Version, compare\n\n\n@pytest.mark.parametrize(\n \"left,right\",\n [\n "
},
{
"path": "tests/test_deprecated_functions.py",
"chars": 2097,
"preview": "from argparse import Namespace\n\nimport pytest\n\nfrom semver import (\n parse,\n parse_version_info,\n match,\n ma"
},
{
"path": "tests/test_docstrings.py",
"chars": 1062,
"preview": "import inspect\n\nimport pytest\n\nimport semver\n\n\ndef getallfunctions(module=semver):\n def getfunctions(_module):\n "
},
{
"path": "tests/test_format.py",
"chars": 2207,
"preview": "import pytest\n\nfrom semver import Version, finalize_version, format_version\n\n\n@pytest.mark.parametrize(\n \"version,exp"
},
{
"path": "tests/test_immutable.py",
"chars": 965,
"preview": "import pytest\n\n\ndef test_immutable_major(version):\n with pytest.raises(AttributeError, match=\"attribute 'major' is re"
},
{
"path": "tests/test_index.py",
"chars": 2988,
"preview": "import pytest\n\nfrom semver import Version\n\n\n@pytest.mark.parametrize(\n \"version, index, expected\",\n [\n # Si"
},
{
"path": "tests/test_match.py",
"chars": 1651,
"preview": "import pytest\n\nfrom semver import match\n\n\ndef test_should_match_simple():\n assert match(\"2.3.7\", \">=2.3.6\") is True\n\n"
},
{
"path": "tests/test_max-min.py",
"chars": 1356,
"preview": "import pytest\n\nfrom semver import max_ver, min_ver\n\n\ndef test_should_get_max():\n assert max_ver(\"3.4.5\", \"4.0.2\") == "
},
{
"path": "tests/test_parsing.py",
"chars": 5503,
"preview": "import pytest\n\nfrom semver import Version, parse, parse_version_info\n\n\n@pytest.mark.parametrize(\n \"version,expected\","
},
{
"path": "tests/test_pysemver-cli.py",
"chars": 4184,
"preview": "from argparse import Namespace\nfrom contextlib import contextmanager\nfrom unittest.mock import patch\n\nimport pytest\n\nfro"
},
{
"path": "tests/test_replace.py",
"chars": 1789,
"preview": "import pytest\n\nfrom semver import Version, replace\n\n\n@pytest.mark.parametrize(\n \"version,parts,expected\",\n [\n "
},
{
"path": "tests/test_semver.py",
"chars": 3676,
"preview": "import pytest # noqa\n\nfrom semver import Version\n\n\n@pytest.mark.parametrize(\n \"string,expected\", [(\"rc\", \"rc\"), (\"rc"
},
{
"path": "tests/test_subclass.py",
"chars": 2307,
"preview": "from semver import Version\nimport pytest\n\n\ndef test_subclass_from_versioninfo():\n class SemVerWithVPrefix(Version):\n "
},
{
"path": "tests/test_typeerror-274.py",
"chars": 341,
"preview": "import pytest\nimport semver\n\n\ndef test_should_work_with_string_and_bytes():\n result = semver.compare(\"1.1.0\", b\"1.2.2"
},
{
"path": "tox.ini",
"chars": 1852,
"preview": "[tox]\nenvlist =\n checks\n py3{7,8,9,10,11,12,13}\nisolated_build = True\nskip_missing_interpreters = True\n\n[gh-action"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the k-bx/python-semver GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 108 files (232.3 KB), approximately 67.6k tokens, and a symbol index with 185 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.