Extract Version
Python module for extracting version from the string or directory name. Could make use for creating an inventory of installed versions of a particular application or finding the latest installed version.
Table of contents
Overview
The module offers the following functionality:
- Fetching version string from the string or name of the directory
- Validating version string
- Sorting config and application directories that contain versions in its name
Supported version shapes
extract_version recognizes exactly two version shapes:
| Shape | Example | Notes |
|---|---|---|
X.Y |
1.0 |
two base-10 integer segments |
X.Y.Z |
2020.1.0 |
three base-10 integer segments |
The full grammar contract:
- Each segment is a base-10 integer (ASCII digits) separated by literal dots.
Spaces, signs, and alphabetic suffixes are not allowed, so
v1.0,1.0beta, and1.0.0abcare rejected. A bare single integer such as1or2020is not a version. - Leading zeros are accepted as input and preserved verbatim in returned
strings (
1.00stays1.00), while sorting compares each segment numerically. - When a string could match more than one shape, the most specific one wins —
the three-segment
X.Y.Zform is preferred over the shorterX.Yfragment. extract_versionmay match a version embedded in a larger string (e.g.PyCharm-2020.1.0→2020.1.0). An explicitpattern(a regex with one capture group) is required only to disambiguate when the string contains more than one numeric candidate, such as a version plus an OS build number (PyCharm-2018.1.2-windows-10.0).
Compatibility policy
Parser hardening keeps the documented happy paths working while tightening a few behaviors that used to be silently permissive. This section records which is which so upgrades hold no surprises.
Preserved for compatibility:
- All previously valid
X.YandX.Y.Zinputs still parse, validate, sort, and extract to the same results (leading zeros are still accepted and preserved). - Permissive embedded extraction stays the default.
extract_versionwith nopatternstill matches a version embedded anywhere inside a larger name (e.g.PyCharm-2020.1.0→2020.1.0). Apatternis required only to disambiguate when a name holds more than one numeric candidate (such as a version plus an OS build number,PyCharm-2018.1.2-windows-10.0) — never for the single-version case. - Duplicate normalized versions resolve deterministically and identically no
matter where the names came from. When two entries extract to different but
normalization-equal version strings (e.g.
1.0and1.0.0, which both normalize to the same numeric key(1, 0, 0), so the string tie-break decides the winner), the winner is a pure function of the version strings, soavailable_versions/get_last_versionreturn the same result whether you passversions_list=orversions_path=. This does not cover two distinct names that extract to the exact same string (e.g.1.0andapp-1.0both extracting to1.0) — there, only one survives in the returned dict, and which one is order-dependent rather than covered by this guarantee.
Intentionally tightened (stricter than before):
validate_versionnow requires a full-string match: trailing or leading non-version characters are rejected. Migration note: code that relied onvalidate_version("1.0.0abc")returning a truthy value now gets"".available_versionsno longer surfaces unparseable names under a blank""key — names whose version cannot be extracted are dropped from the returned mapping, so the result contains only valid versions.get_last_versionraises a descriptiveValueErrorwhen the inventory is empty or every entry is unparseable, instead of failing with an opaque error.
Dependencies
extract_version itself is dependency-free at runtime — it only uses the Python standard library, so
pip install extract-version pulls in nothing else.
Building, testing, and releasing the project locally pull in a few additional tools, scoped to what you're doing:
| Purpose | Tools | Needed for |
|---|---|---|
| Running tests | pytest, pytest-cov |
tests/{unit,integration,mock,e2e}/ |
| Building the wheel | release-saga (>= 1.2.0, < 2) |
release-saga |
| Publishing a release | aws, gh, twine |
release-saga --upload-s3 / --create-release / --publish-pypi (see Releasing) |
Requires Python >= 3.11.
Setup
Install from PyPI
pip install extract-version
Install from a release archive
Archives of previous releases are available on GitHub, with a mirror on AWS.
Install for development
pip install -e .
This registers the extract-version console script against your working copy of src/extract_version/, so
edits take effect immediately without reinstalling.
Usage
Library
Install the package, then import and call the functions directly — see Examples below for the
functions in action: extract_version, validate_version, sort_versions, available_versions, and
get_last_version.
Command line
Installing the package also provides an extract-version command (equivalently, python -m extract_version)
exposing the same functionality from the shell. Every subcommand also reads its input from stdin when no
arguments are given, so it composes with tools like ls/find.
extract-version extract "PyCharm-2020.1.0"
> 2020.1.0
extract-version extract "PyCharm-2018.1.2-windows-10.0" --pattern "PyCharm-(.*)-windows-10.0"
> 2018.1.2
extract-version sort 2018.1.2 2018.2.0 2020.1.0
> 2018.1.2
> 2018.2.0
> 2020.1.0
ls "C:/Users/user/AppData/Local/JetBrains/PyCharm" | extract-version last-version --pattern "PyCharm-(.*)"
> PyCharm-2020.1.0
extract-version available --path "C:/Users/user/AppData/Local/JetBrains/PyCharm" --json
> {"2018.1.2": "PyCharm-2018.1.2", "2018.2.0": "PyCharm-2018.2.0", "2020.1.0": "PyCharm-2020.1.0"}
Run extract-version --help or extract-version <subcommand> --help for the full option list.
Output format
stdout carries only successful results (diagnostics always go to stderr, never mixed in-band), in one of three shapes:
- Scalar —
last-version, andsortwhen given a single value: the value on one line. - List —
extract,validate, andsort's list output: one item per line. - Version-to-name mapping —
available: oneversion<TAB>nameline per entry, ordered by the numeric version-sort contract (so1.9precedes1.10), not lexicographically by string.
--json serializes the same logical content (a JSON scalar, array, or object).
The available object's keys appear in the same numeric version order as the
plain-text lines. For partial-success commands (extract/validate), stdout
holds only the values that succeeded; failed inputs are reported solely on
stderr and never appear in the stdout output.
Examples
1. Extracting a version from a string
We fetch version 2020.1.0 from string PyCharm-2020.1.0, or version 1.0 from my_program_v1.0.
AppVersion.Major.Minorversion format
extract_version(version_string="PyCharm-2020.1.0")
> "2020.1.0"
Major.Minorversion format
extract_version(version_string="my_program_v1.0")
> "1.0"
2. Sorting versioned directories
sort_versions(["PyCharm-2018.1.2", "PyCharm-2018.2.0", "PyCharm-2020.1.0"])
> ["PyCharm-2018.1.2", "PyCharm-2018.2.0", "PyCharm-2020.1.0"]
3. Disambiguating with a pattern
In edge cases with more than one pattern present, e.g. PyCharm-2018.1.2-windows-10.0, we should provide a
clue where the version should be extracted from, in a form of a pattern-regex, e.g.
PyCharm-(.*)-windows-10.0. The call of such function may look like this:
extract_version(version_string='PyCharm-2018.1.2-windows-10.0', pattern='PyCharm-(.*)-windows-10.0')
> "2018.1.2"
Pattern error contract
When called with a pattern, extract_version behaves in one of four ways:
- No match — the pattern finds nothing in the string: returns
""(this is "no version found", not a caller error). - Wrong capture-group count — the pattern matches but does not declare
exactly one capture group (zero groups, e.g. a plain or non-capturing
(?:...)pattern, or two or more): raisesPatternArityErrorwith a message likepattern must contain exactly one capture group.PatternArityErroris aValueErrorsubclass importable fromextract_version.version_info, so existingexcept ValueErrorhandlers keep catching it, while callers that need to single out this case can catch it directly. - Invalid regex — the
patternstring is not valid regex syntax: Python's ownre.errorpropagates unchanged (it is not caught or converted). - Captured text isn't a version — the pattern matches with exactly one
capture group, but the captured text fails version validation: returns
"".
The CLI (extract, available, and last-version with --pattern) catches
both PatternArityError and re.error and reports them as a friendly stderr
message with exit code 1, rather than surfacing a raw Python traceback.
4. Building an inventory of installed versions
application_path = "C:/Users/user/AppData/Local/JetBrains/PyCharm"
pycharm_versions = available_versions(versions_path=application_path)
> {"2018.1.2": "PyCharm-2018.1.2", "2018.2.0": "PyCharm-2018.2.0", "2020.1.0": "PyCharm-2020.1.0"}
Releasing
release-saga always builds the wheel first, can install it locally (--local-install, or
--local-dev-mode for an editable install), and can optionally publish it via --upload-s3,
--create-release (GitHub), and --publish-pypi. Install it first with
pip install "release-saga>=1.2.0,<2" — it is a separate PyPI package, not bundled with this project
(it is also listed in the dev dependency group in pyproject.toml). Version 1.2.0 removed the old
--mode flag, so older release-saga --mode ... invocations no longer work.
Release tool prerequisites
Each publishing flag needs its own external tool and credentials, installed and configured beforehand:
| Flag | Tool | Credentials |
|---|---|---|
--upload-s3 |
aws |
aws configure (or an AWS_PROFILE/instance role) |
--create-release |
gh |
gh auth login |
--publish-pypi |
twine |
a PyPI API token in ~/.pypirc |
Run python scripts/install_deps.py to install aws, gh, and twine (cross-platform:
pip-installs awscli/twine everywhere, and uses Homebrew/winget/apt for gh where available).
It does not set up credentials — do that with the commands in the table above.
Running a release
Run
release-saga --create-release --upload-s3 --publish-pypi
to perform the full release cycle.
Rollback behavior
If a tool or its credentials aren't ready when release-saga reaches that step, the step
reports itself unavailable and the run rolls back every step already completed (deletes the S3
object, the git tag, and/or the GitHub release, in that order) before exiting — see
ReleaseStep/run_release_pipeline() in the release-saga package.
Metadata
Release files for extract-version 1.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| extract_version-1.4.0.tar.gz | 21.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| extract_version-1.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 40.3 kB
Release files / extract_version-1.4.0.tar.gz
| Download URL | extract_version-1.4.0.tar.gz |
|---|---|
| Size | 21.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
68c9cb2b01abbc23e05b5600ff8569d3334d0a0c9c06150f07ee9685ace2385f
|
|
BLAKE2b-256 checksum How to use checksums |
c871fd77475367678b74e9dd6436e8b525803b8f8bf226937fd07ee4dc2866d3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.13
|
Release files / extract_version-1.4.0-py3-none-any.whl
| Download URL | extract_version-1.4.0-py3-none-any.whl |
|---|---|
| Size | 18.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ac86872d3b34a325db06047a31fa1aec6aa728a28d14e5d76caa6d258c32318a
|
|
BLAKE2b-256 checksum How to use checksums |
88c757711f5f68f5a35d44a1d5f81570146fc45a53a050fdc2c3f619c70272f6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.13
|
Security for extract-version 1.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.