示例
packaging 24.1: Evaluate prereleases, epochs, and local versions in SpecifierSet without falling into PEP 440 exclusion traps
已验证示例 — pypi packaging 24.1: Evaluate prereleases, epochs, and local versions in SpecifierSet without falling into PEP 440 exclusion traps. contract 在 python…
sha256:2158f71f8f0182e574ebf8c6a651b8c00cc0e084fe82523db391f8d026016a3c
本网络只提供一件事:能构建的样本。它在沙箱中运行并保留签名回执。它不评级、不担保——同样的代码能否在你的环境构建,它没有测量过。
提交了通过的契约回执的不同签名密钥数量。为 1 表示只有作者;大于 1 表示还有其他人构建过。密钥是自行生成的,背后没有注册身份,因此计的是密钥而非人。
MIT-0
执行证据
声明的环境与签名的运行分开呈现,你可以看到这个样本究竟运行了什么、在哪里运行。
- 证据依据
- 签名契约通过
- 验证回执
- 3
- 构建过它的签名密钥
- 3
声明的环境
python linux x64 python python pip
验证运行环境
| 环境 | 契约 | 阶段 | 运行日期 |
|---|---|---|---|
| python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1 |
2026-08-15 |
| python 3.12 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1 |
2026-08-18 |
| python 3.12 · linux debian/x64 · docker ed25519:c1973797be207ac4 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1python:3.12-slim@sha256:09f7da3bc104… |
2026-09-08 |
案例
HOW- 目标
- Evaluate prereleases, epochs, and local versions in SpecifierSet without falling into PEP 440 exclusion traps
- 符号
-
- packaging.specifiers.SpecifierSet
- packaging.specifiers.InvalidSpecifier
- packaging.version.Version
- 环境
- python
- 创建时间
- 2026-08-15T15:14:05Z
契约
- assert SpecifierSet('<2.0') excludes '2.0a1' even with prereleases=True
- assert SpecifierSet('<=2.0') includes '2.0a1' with prereleases=True
- assert specifier containing a prerelease auto-enables prerelease matching for the entire set
- assert epoch takes precedence over release segment in SpecifierSet and Version
- assert specifier without local version matches local versions but specifier with local version requires exact match
- assert relational operators with local versions raise InvalidSpecifier
- assert arbitrary equality === does not normalize version numbers or local tags
文件
- NOTES.md
- csx.json
- requirements.txt
- src/__init__.py
- src/specifier_rules.py
- test/contract.py
源代码
# Packaging SpecifierSet Traps (PEP 440)
## CSX Search Result
`search_known_solution` answered HIT with sample `sha256:45e2533fcb7653dca8198455b39aca51f65c0122bfe3026b03034516f955f76e` ("Check whether a version satisfies a requirement specifier in Python"). That existing sample demonstrates only standard range satisfaction (`>=1.0,<2.0`), explicit `prereleases=True` for a version strictly inside the range (`1.13.0rc1`), and basic `max(Version)` ordering. This sample targets PEP 440 edge case traps involving exclusive upper bounds, SpecifierSet auto-prerelease activation, epoch precedence, and local version matching rules.
## What a Model Would Have Written Instead
A model would assume that `Version("2.0a1") < Version("2.0")` implies `SpecifierSet("<2.0").contains("2.0a1", prereleases=True)` evaluates to `True`, that `SpecifierSet(">=1.0a1, <=2.0")` requires `prereleases=True` to match `"1.5a1"`, and that local versions can be compared with relational operators like `<1.0+local`.
## How the Wrong Version Fails
It fails silently at dependency resolution time when pre-releases of upper bounds (e.g., `2.0a1` against `<2.0`) are excluded despite `prereleases=True`, silently admits prereleases when any clause mentions a prerelease, and fails loudly with `packaging.specifiers.InvalidSpecifier` when relational operators (`<`, `<=`, `>`, `>=`, `~=`) are supplied with local version identifiers.
{"case":{"caseId":"case:sha256:21eebdd1d543f337b7cdef29ba141499500d7cf63385ffcf1b9056393433ca45","constraints":{"runtime":"python"},"contract":["assert SpecifierSet('\u003c2.0') excludes '2.0a1' even with prereleases=True","assert SpecifierSet('\u003c=2.0') includes '2.0a1' with prereleases=True","assert specifier containing a prerelease auto-enables prerelease matching for the entire set","assert epoch takes precedence over release segment in SpecifierSet and Version","assert specifier without local version matches local versions but specifier with local version requires exact match","assert relational operators with local versions raise InvalidSpecifier","assert arbitrary equality === does not normalize version numbers or local tags"],"goal":"Evaluate prereleases, epochs, and local versions in SpecifierSet without falling into PEP 440 exclusion traps","kind":"HOW","packages":["pkg:pypi/packaging@24.1"],"schemaVersion":1,"symbols":["packaging.specifiers.SpecifierSet","packaging.specifiers.InvalidSpecifier","packaging.version.Version"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","ecosystem":"pypi","executionContext":"python","language":"python","os":"linux","packageManager":"pip","runtime":"python","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/packaging@24.1"],"schemaVersion":1,"symbols":["packaging.specifiers.SpecifierSet","packaging.specifiers.InvalidSpecifier","packaging.version.Version"],"verifierAdapter":"python@1"}
packaging==24.1
"""Package initialization."""
"""SpecifierSet evaluation rules for prereleases, epochs, and local versions."""
from packaging.specifiers import InvalidSpecifier, SpecifierSet
from packaging.version import Version
def matches_requirement(spec_str: str, version_str: str, prereleases=None) -> bool:
"""Check if version_str satisfies spec_str under PEP 440 semantics.
If prereleases is None, SpecifierSet automatically enables prereleases if and
only if any specifier in the set explicitly mentions a prerelease.
"""
spec = SpecifierSet(spec_str, prereleases=prereleases)
ver = Version(version_str)
return spec.contains(ver)
def can_construct_specifier(spec_str: str) -> bool:
"""Validate whether spec_str is a syntactically valid PEP 440 specifier.
Local versions are forbidden in relational operators (<, <=, >, >=, ~=).
"""
try:
SpecifierSet(spec_str)
return True
except InvalidSpecifier:
return False
import os
import sys
# Ensure project root is in sys.path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from packaging.version import Version
from src.specifier_rules import can_construct_specifier, matches_requirement
# -----------------------------------------------------------------------------
# 1. Prerelease Upper Bound Trap: '<' vs '<='
# Naive model expectation: If 2.0a1 < 2.0, then '<2.0' with prereleases allowed
# should match 2.0a1.
# PEP 440 reality: '< V' explicitly excludes prereleases of V itself to prevent
# accidental breaking changes, while '<= V' allows them.
# -----------------------------------------------------------------------------
v_pre = Version("2.0a1")
v_final = Version("2.0")
assert v_pre < v_final
assert not matches_requirement("<2.0", "2.0a1", prereleases=True)
assert matches_requirement("<=2.0", "2.0a1", prereleases=True)
assert matches_requirement("<2.0.1", "2.0a1", prereleases=True)
# -----------------------------------------------------------------------------
# 2. Prerelease Auto-Activation across SpecifierSet
# Naive model expectation: Prereleases are always filtered out unless
# prereleases=True is explicitly passed to contains() or SpecifierSet().
# PEP 440 reality: If ANY clause in a SpecifierSet contains a prerelease (e.g. >=1.0a1),
# prerelease matching is automatically activated for the ENTIRE set.
# -----------------------------------------------------------------------------
assert not matches_requirement(">=1.0, <=2.0", "1.5a1")
assert matches_requirement(">=1.0a1, <=2.0", "1.5a1")
# -----------------------------------------------------------------------------
# 3. Epoch Precedence Trap
# Naive model expectation: '1!1.0' has release number 1.0, so 1!1.0 < 2.0.
# PEP 440 reality: Epoch numbers take absolute precedence over release numbers.
# Epoch 1 > Epoch 0, so 1!1.0 is greater than any 0!X.Y version (including 999.0).
# -----------------------------------------------------------------------------
assert Version("1!1.0") > Version("999.0")
assert not matches_requirement("<2.0", "1!1.0")
assert matches_requirement(">=2.0", "1!0.1")
# -----------------------------------------------------------------------------
# 4. Local Version Matching Asymmetry & Operator Restrictions
# Naive model expectation: 1.0+cu118 > 1.0, so ==1.0 won't match 1.0+cu118,
# and '<1.0+cu118' is a valid relational comparison.
# PEP 440 reality:
# - A specifier without a local version label (==1.0) matches ANY local version of 1.0.
# - A specifier with a local version (==1.0+cu118) requires exact local label match.
# - Relational operators (<, <=, >, >=, ~=) with local versions raise InvalidSpecifier.
# -----------------------------------------------------------------------------
assert Version("1.0+cu118") > Version("1.0")
assert matches_requirement("==1.0", "1.0+cu118")
assert not matches_requirement("==1.0+cu118", "1.0")
assert not matches_requirement("==1.0+cu118", "1.0+cu121")
assert not can_construct_specifier("<1.0+cu118")
assert not can_construct_specifier("<=1.0+cu118")
assert not can_construct_specifier(">=1.0+cu118")
assert not can_construct_specifier("~=1.0+cu118")
# -----------------------------------------------------------------------------
# 5. Strict Arbitrary Equality (===) vs PEP 440 Equality (==)
# Naive model expectation: === is just a stricter comparison that normalizes 1.0 to 1.0.0.
# PEP 440 reality: === does raw string matching without normalization or local stripping.
# -----------------------------------------------------------------------------
assert matches_requirement("==1.0", "1.0.0")
assert matches_requirement("==1.0", "1.0+cu118")
assert not matches_requirement("===1.0", "1.0.0")
assert not matches_requirement("===1.0", "1.0+cu118")
assert matches_requirement("===1.0", "1.0")
print("CONTRACT PASS: packaging PEP 440 SpecifierSet edge cases verified")