Tighten the comments on the PyPI metadata guards
This commit is contained in:
parent
148de9f970
commit
d838353e9f
3 changed files with 32 additions and 53 deletions
|
|
@ -59,8 +59,7 @@ dependencies = [
|
|||
# imports that, so every command needs it. typer supplied it until 0.27
|
||||
# dropped the dependency, which left this satisfied only by chance.
|
||||
"click>=8.0",
|
||||
# unsloth_cli/__init__.py imports commands/chat.py, which imports rich at
|
||||
# module level, so every command needs it. typer still pulls rich, but it
|
||||
# Imported at module level on the CLI entry path. typer still pulls rich, but it
|
||||
# pulled click too until 0.26.0 dropped it: same shape, so declare it.
|
||||
"rich",
|
||||
]
|
||||
|
|
@ -157,12 +156,9 @@ huggingface = [
|
|||
"pyyaml",
|
||||
"nest-asyncio",
|
||||
]
|
||||
# "notorch" means this list declares no torch of its own, not that installing it
|
||||
# leaves you torch-free: extras are additive to [project].dependencies, which on
|
||||
# this branch deliberately carry the full runtime so a bare `pip install unsloth`
|
||||
# works, and accelerate/peft/sentence-transformers each require torch anyway. The
|
||||
# genuinely torch-free path is the installer's --no-deps pass over
|
||||
# studio/backend/requirements/no-torch-runtime.txt, not plain pip resolution.
|
||||
# "notorch" means this list declares no torch, not that you end up torch-free: extras
|
||||
# are additive to [project].dependencies, which carry the full runtime here. The
|
||||
# torch-free path is the installer's --no-deps pass over no-torch-runtime.txt, not pip.
|
||||
# Kept byte-identical to main so the two branches can be diffed for drift.
|
||||
huggingfacenotorch = [
|
||||
"unsloth_zoo>=2026.7.6",
|
||||
|
|
@ -184,17 +180,13 @@ huggingfacenotorch = [
|
|||
"trl>=0.18.2,!=0.19.0,<=0.24.0",
|
||||
"sentence-transformers",
|
||||
]
|
||||
# What this extra does on this branch is raise the bitsandbytes floor; it does not
|
||||
# hand you a ROCm stack. The base dependencies still apply, so ROCm torch has to
|
||||
# come from the AMD index or already be installed (pip leaves an installed
|
||||
# +rocm torch alone, since it satisfies the base specifier). Use install.sh or
|
||||
# `unsloth studio install` for a torch-free AMD install.
|
||||
# Raises the bitsandbytes floor; it does not hand you a ROCm stack. The base deps still
|
||||
# apply, so ROCm torch must come from the AMD index or be installed already (pip leaves
|
||||
# a +rocm torch alone). Use install.sh or `unsloth studio install` for a torch-free AMD install.
|
||||
amd = [
|
||||
"unsloth[huggingfacenotorch]",
|
||||
# 4-bit decode is unreliable on ROCm before 0.50.0, the first PyPI release
|
||||
# carrying the full path: blocksize/warp decoupling (bnb #1887), fused SIMT
|
||||
# GEMM on RDNA (#1979), RDNA3/4 workgroup fix (#2012). Keep in step with
|
||||
# install.sh and studio/install_python_stack.py.
|
||||
# 4-bit decode is unreliable on ROCm before 0.50.0 (bnb #1887, #1979, #2012).
|
||||
# Keep in step with install.sh and studio/install_python_stack.py.
|
||||
"bitsandbytes>=0.50.0 ; ('linux' in sys_platform) and (platform_machine == 'AMD64' or platform_machine == 'x86_64' or platform_machine == 'aarch64')",
|
||||
"bitsandbytes>=0.50.0 ; (sys_platform == 'win32') and (platform_machine == 'AMD64' or platform_machine == 'x86_64')",
|
||||
]
|
||||
|
|
@ -556,12 +548,10 @@ flashattention = [
|
|||
"ninja ; ('linux' in sys_platform)",
|
||||
"flash-attn>=2.6.3 ; ('linux' in sys_platform)",
|
||||
]
|
||||
# torchcodec is the audio decode path; its releases track torch minor versions,
|
||||
# so each torch minor gets its own window (#7225). It publishes no sdist and only
|
||||
# manylinux_2_28_x86_64, macosx_*_arm64 and win_amd64 wheels, so Linux aarch64,
|
||||
# Windows ARM64 and Intel Mac have nothing to resolve and pip would hard-fail the
|
||||
# whole install. Gate on the platforms that have a wheel, matching
|
||||
# PLATFORM_LACKS_TORCHCODEC_WHEEL in studio/install_python_stack.py.
|
||||
# torchcodec releases track torch minor versions, so each torch minor gets its own
|
||||
# window (#7225). It ships no sdist and only x86_64 linux/windows and arm64 macOS
|
||||
# wheels, so elsewhere pip would hard-fail the whole install: gate on the platforms
|
||||
# with a wheel, matching PLATFORM_LACKS_TORCHCODEC_WHEEL in install_python_stack.py.
|
||||
audio-torch280 = [
|
||||
"torchcodec>=0.6.0,<0.8.0 ; python_version >= '3.9' and (((sys_platform == 'linux' or sys_platform == 'win32') and (platform_machine == 'x86_64' or platform_machine == 'AMD64')) or (sys_platform == 'darwin' and platform_machine == 'arm64'))",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -3,14 +3,10 @@
|
|||
|
||||
"""This branch is what gets uploaded to PyPI, so its metadata has to satisfy PyPI's rules.
|
||||
|
||||
PyPI rejects any PEP 508 direct reference in ``Requires-Dist`` with
|
||||
``Invalid value for requires_dist. Error: Can't have direct dependency``
|
||||
(pypi/warehouse#7136), and ``twine check`` does not catch it beforehand
|
||||
(pypa/twine#726) -- the upload just 400s. ``main`` carries hundreds of direct URL
|
||||
requirements for the CUDA/XPU/ROCm wheel indexes, so every merge from ``main`` is a
|
||||
chance to import one here and break publishing. Nothing else guards that.
|
||||
|
||||
Offline by design: structural checks only, no network.
|
||||
PyPI rejects any PEP 508 direct reference in ``Requires-Dist`` (pypi/warehouse#7136) and
|
||||
``twine check`` does not catch it (pypa/twine#726): the upload just 400s. ``main`` carries
|
||||
hundreds of direct URL requirements for the wheel indexes, so every merge from it can
|
||||
break publishing, and nothing else guards that. Structural checks only, no network.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -43,13 +39,10 @@ def _all_requirements() -> list[tuple[str, str]]:
|
|||
|
||||
class TestNoDirectUrlRequirements:
|
||||
def test_no_requirement_uses_a_direct_url(self):
|
||||
"""Ask packaging, not the spelling.
|
||||
|
||||
PEP 508 allows any whitespace around the `@`, and the scheme is case
|
||||
insensitive, so `flash-attn@https://...` and `flash-attn @ HTTPS://...` are both
|
||||
valid direct references that a substring match on " @ https://" would wave
|
||||
through - the exact upload failure this guards. `Requirement.url` is set for
|
||||
every form of them.
|
||||
"""Parse rather than substring-match: PEP 508 allows any whitespace around the
|
||||
`@` and a case-insensitive scheme, so `flash-attn@https://...` and
|
||||
`flash-attn @ HTTPS://...` both slip past a " @ https://" match.
|
||||
`Requirement.url` is set for every form.
|
||||
"""
|
||||
packaging_requirements = pytest.importorskip("packaging.requirements")
|
||||
offenders = []
|
||||
|
|
@ -78,12 +71,11 @@ class TestNoDirectUrlRequirements:
|
|||
|
||||
class TestExtraReferencesResolve:
|
||||
def test_every_unsloth_extra_reference_exists(self):
|
||||
"""A `unsloth[foo]` pointing at an extra this branch does not define installs
|
||||
nothing and fails silently, which is how a partially ported extras block breaks.
|
||||
"""A `unsloth[foo]` naming an undefined extra installs nothing and fails silently.
|
||||
|
||||
Parsed and canonicalized rather than pattern-matched: project names and extra
|
||||
names are both case and separator insensitive (PEP 503, PEP 685), so pip honours
|
||||
`Unsloth[Rocm72_Torch2100]` while a lowercase regex would never look at it.
|
||||
Canonicalized on both sides: project and extra names are case and separator
|
||||
insensitive (PEP 503, PEP 685), so pip honours `Unsloth[Rocm72_Torch2100]`
|
||||
while a lowercase regex would never look at it.
|
||||
"""
|
||||
packaging_requirements = pytest.importorskip("packaging.requirements")
|
||||
packaging_utils = pytest.importorskip("packaging.utils")
|
||||
|
|
@ -108,7 +100,7 @@ class TestExtraReferencesResolve:
|
|||
|
||||
class TestAmdExtraIsInstallableFromPyPI:
|
||||
"""`pip install unsloth[amd]` is the supported AMD entry point, so the extra has to
|
||||
exist here and stay a version floor -- a URL pin would be unpublishable."""
|
||||
exist here and stay a version floor: a URL pin would be unpublishable."""
|
||||
|
||||
def test_amd_extra_exists_and_floors_bitsandbytes(self):
|
||||
extras = _load()["project"].get("optional-dependencies", {})
|
||||
|
|
@ -127,9 +119,8 @@ class TestAmdExtraIsInstallableFromPyPI:
|
|||
|
||||
|
||||
class TestRuntimeImportsAreDeclared:
|
||||
"""This branch's base install has to satisfy every module-scope import on the CLI
|
||||
entry path. typer supplied click until 0.26.0 dropped it (#7504); it still supplies
|
||||
rich, so rich is satisfied only by chance unless we declare it ourselves."""
|
||||
"""The base install must cover every module-scope import on the CLI entry path:
|
||||
typer supplied click until 0.26.0 dropped it (#7504), and supplies rich by chance."""
|
||||
|
||||
ENTRY_PATH_IMPORTS = ("click", "rich", "structlog", "typer")
|
||||
|
||||
|
|
@ -147,9 +138,8 @@ class TestRuntimeImportsAreDeclared:
|
|||
|
||||
|
||||
class TestAcceleratorExtrasCarryTheirCompanions:
|
||||
"""Each accelerator extra composes a stack: `-ampere-` variants add flash-attn and
|
||||
torch 2.10 variants add the torchcodec audio path. A variant that silently drops one
|
||||
installs a quietly weaker environment than its siblings."""
|
||||
"""`-ampere-` variants add flash-attn and torch 2.10 variants add the torchcodec
|
||||
audio path; dropping one installs a quietly weaker stack than its siblings."""
|
||||
|
||||
def test_torch2100_extras_pull_the_audio_path(self):
|
||||
extras = _load()["project"].get("optional-dependencies", {})
|
||||
|
|
|
|||
|
|
@ -45,9 +45,8 @@ def _stub_torch(monkeypatch, version: str):
|
|||
|
||||
|
||||
def test_torch210_extras_bundle_audio_torch210():
|
||||
"""The ROCm extras pin torch from the AMD wheel index, which PyPI rejects as a
|
||||
direct reference, so this branch does not carry them. Check whichever torch 2.10
|
||||
extras it does define, and require at least one."""
|
||||
"""ROCm extras pin torch by direct URL, which PyPI rejects, so this branch omits
|
||||
them: check whichever torch 2.10 extras it defines, and require at least one."""
|
||||
text = PYPROJECT.read_text(encoding = "utf-8")
|
||||
checked = 0
|
||||
for extra in (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue