lib.version.Dependency: Reject unsupported spec operators
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 4m27s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m54s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 4m0s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m17s
CI / Packaging test (push) Successful in 0s

The spec split pattern ([=><]+) tokenizes only the =, > and < characters,
so a PEP 440 operator like ~= or != leaves its ~ or ! glued to the package
name: Dependency('pkg~=1.0') parses to base name 'pkg~' with operator '='.
App.__get_project_refs() carries the same pattern inline, so the same specs
corrupt the module name used for the -devel subpackage check. The split
also tolerates operator strings the language does not support, most visibly
==, which parses and renders but raises NotImplementedError only when
expansion is requested.

The spec language supports exactly =, <, <=, > and >=, and boundary
expansion implements all of them. Extend the split pattern with ~ and ! so
that foreign operators tokenize as operator strings, and reject every
operator outside the supported set in Dependency.__parsed_spec() with
Dependency.Error, naming the supported operators. Route
App.__get_project_refs() through Dependency for the name and module parts
instead of the second inline split, so the validation lives in one place.
The catch all in Dependency.__version_boundaries() stays as a backstop
against drift between the allow list and the expansion cases.

The tests drop == from the accepted operator loop, drop the now-unreachable
not-expandable case, and assert that ~=, !=, ~, ==, ===, << and >> are
rejected at parse time with a message naming the operator.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.85.1
Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2026-09-11 10:48:21 +02:00
commit c31dfb4bbf
Signed by: Jan Lindemann
GPG key ID: 3750640C9E25DD61
3 changed files with 31 additions and 12 deletions

View file

@ -209,8 +209,9 @@ class App(Base):
scope: Scope,
names_only: bool,
) -> None:
name = self.strip_module_from_spec(spec)
mod = re.split('([=><]+)', spec)[0].strip()
dep = Dependency(spec)
name = dep.base_name
mod = dep.full_name
if names_only:
spec = name
if spec in buf:

View file

@ -22,7 +22,7 @@ class Dependency: # export
class Error(ValueError):
pass
__SPLIT_RE: ClassVar[re.Pattern[str]] = re.compile('([=><]+)')
__SPLIT_RE: ClassVar[re.Pattern[str]] = re.compile('([~=><!]+)')
@property
def __target_prefix(self) -> str:
@ -45,6 +45,12 @@ class Dependency: # export
case 3:
if not parts[0] or not parts[2]:
self.__raise(f'Invalid dependency spec "{self.__spec}"')
if parts[1] not in ('=', '<', '<=', '>', '>='):
self.__raise(
f'Spec "{self.__spec}": unsupported operator '
f'"{parts[1]}", supported operators are =, <, '
'<=, > and >='
)
if parts[2] == 'REVISION':
self.__raise(
f'Spec "{self.__spec}": a bare REVISION renders as a '