Semantic Versioning (Semver)
Relevant source files
The following files were used as context for generating this wiki page:
Semantic Versioning (Semver) in dbt-common provides a robust framework for parsing, comparing, and resolving software version strings based on the Semantic Versioning specification. It is primarily used to manage dependencies between dbt-core, adapter implementations, and internal utility updates, ensuring that compatible versions are selected during execution.
The module implements complex logic for handling version ranges, pre-releases, and build metadata. It leverages structured dataclasses and regular expressions to validate version strings and provides utilities to reduce multiple version constraints into a single compatible range.
Sources: dbt_common/semver.py:1-15, CHANGELOG.md:1-10
Core Data Structures
The system relies on a set of dataclasses that represent versions and their comparison logic. These classes inherit from dbtClassMixin for schema validation and serialization support.
Version Specification Classes
- VersionSpecification: The base data structure holding the constituent parts of a version (major, minor, patch, prerelease, build) and a comparison matcher.
- VersionSpecifier: Extends the specification with comparison logic (
compare,__lt__,__gt__) and string serialization. - VersionRange: Represents a bound or unbound range defined by a
startandendVersionSpecifier. - UnboundedVersionSpecifier: A special case representing a wildcard or "any" version constraint.
Sources: dbt_common/semver.py:18-40, dbt_common/semver.py:76-85, dbt_common/semver.py:209-215, dbt_common/dataclass_schema.py:64-70
Comparison Matchers
The Matchers enum defines the supported operators for version constraints:
| Matcher | Symbol | Description |
|---|---|---|
GREATER_THAN | > | Version must be strictly greater than specified |
GREATER_THAN_OR_EQUAL | >= | Version must be greater than or equal to specified |
LESS_THAN | < | Version must be strictly less than specified |
LESS_THAN_OR_EQUAL | <= | Version must be less than or equal to specified |
EXACT | = | Version must match exactly |
Sources: dbt_common/semver.py:11-17
Architecture and Logic Flow
The versioning logic follows a structured path from string parsing to range reduction.
Version Parsing Flow
When a version string is processed, it is validated against a verbose regular expression that decomposes the string into its semantic components.
The parsing logic ensures that numeric components do not have leading zeros (except for '0' itself) and correctly identifies pre-release tags and build metadata.
Sources: dbt_common/semver.py:43-74, dbt_common/semver.py:91-105
Range Reduction Logic
reduce_versions is a critical function that takes multiple version constraints and attempts to find a single VersionRange that satisfies all of them.
Comparison Mechanics
Comparison involves checking major, minor, and patch segments as integers. Pre-release tags are compared using "natural" comparison (alphanumeric parts).
Pre-release Handling
Pre-releases are considered "less than" their corresponding stable release. For example, 1.0.0-alpha is less than 1.0.0. The _nat_cmp helper function handles the dotted parts of pre-release strings, treating numeric parts as integers to ensure 1.0.0-alpha.10 is greater than 1.0.0-alpha.2.
Sources: dbt_common/semver.py:116-136, dbt_common/semver.py:188-206
Compatibility Matrix logic
The versions_compatible function serves as a boolean check to determine if a set of version requirements can be satisfied simultaneously.
Sources: dbt_common/semver.py:298-307, tests/unit/test_semver.py:46-51
Key Functions Summary
| Function | Description | Source |
|---|---|---|
from_version_string(cls, version_string) | Class method to create a specifier from a raw string. | semver.py:91 |
reduce_versions(*args) | Combines multiple version requirements into a single compatible range. | semver.py:257 |
resolve_to_specific_version(range, available) | Finds the highest available version string that fits within a range. | semver.py:321 |
filter_installable(versions, install_prerelease) | Filters a list of versions to exclude pre-releases if requested. | semver.py:339 |
find_possible_versions(range, available) | Returns all versions from a list that are compatible with the range. | semver.py:310 |
Error Handling
The module defines specific exceptions for versioning failures, found in both the semver module and the base exceptions file.
- SemverError: Base exception for all semantic versioning issues.
- VersionsNotCompatibleError: Raised when two or more version constraints have no overlapping compatible versions (e.g.,
>2.0.0and<1.0.0).
Sources: dbt_common/exceptions/base.py:186-192, dbt_common/semver.py:93-96, dbt_common/semver.py:291-294
Implementation Example: Version Resolution
The following logic demonstrates how a range is reduced and then resolved to a specific version from a list of candidates.
# Sources: tests/unit/test_semver.py:126-150
requested_range = create_range(">=1.0.0", "<1.2.0")
available = ["1.0.0", "1.1.0a1", "1.1.0", "1.2.0a1", "1.2.0"]
# This would resolve to "1.1.0" as it is the highest
# stable version within the specified range bounds.
result = resolve_to_specific_version(requested_range, available)
Summary
The Semantic Versioning system in dbt-common provides the necessary infrastructure for reliable dependency management. By using strict regex parsing and mathematical range reduction, it prevents version conflicts between dbt components. It supports advanced features like "natural" pre-release sorting and unbounded wildcards, making it suitable for complex plugin and adapter ecosystems.
Sources: dbt_common/semver.py