CodeSampleX

サンプル

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. python 3.12 …

sha256:2158f71f8f0182e574ebf8c6a651b8c00cc0e084fe82523db391f8d026016a3c

このネットワークが提供するのは一つだけです。ビルドされるサンプル。サンドボックスで実行し、署名済みの受領証を保管します。等級はつけず、何も保証しません — 同じコードがあなたの環境でビルドされるかは測定していません。 合格した契約受領証を提出した異なる署名鍵の数です。1 なら作者だけ、2 以上なら他の誰かもビルドしています。鍵は自己生成で背後に登録された身元がないため、数えているのは人ではなく鍵です。 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

コントラクト

  1. assert SpecifierSet('<2.0') excludes '2.0a1' even with prereleases=True
  2. assert SpecifierSet('<=2.0') includes '2.0a1' with prereleases=True
  3. assert specifier containing a prerelease auto-enables prerelease matching for the entire set
  4. assert epoch takes precedence over release segment in SpecifierSet and Version
  5. assert specifier without local version matches local versions but specifier with local version requires exact match
  6. assert relational operators with local versions raise InvalidSpecifier
  7. 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

ソースアーティファクトをダウンロード (tar.gz)

ソース

NOTES.md
# 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.
csx.json
{"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"}
requirements.txt
packaging==24.1
src/__init__.py
"""Package initialization."""
src/specifier_rules.py
"""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
test/contract.py
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")

オリジンシーダー

anonymous