From 225b4ec456b4dd9141af0cce29ba307d2709ad26 Mon Sep 17 00:00:00 2001 From: janware DevOps Date: Thu, 10 Sep 2026 23:10:51 +0000 Subject: [PATCH 1/5] Start version: 1.0.0-262 Signed-off-by: janware DevOps --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 3f5d3bf1..e77026b9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0-261-dev +1.0.0-262-dev From 150f9f1bbcad859efcd45d615e3ec0332da5f688 Mon Sep 17 00:00:00 2001 From: janware DevOps Date: Thu, 10 Sep 2026 23:12:01 +0000 Subject: [PATCH 2/5] Release 1.0.0-262@kali-rolling/amd64 Signed-off-by: janware DevOps --- HASH | 2 +- RELEASES | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HASH b/HASH index 4ae336f2..9c8bcf9b 100644 --- a/HASH +++ b/HASH @@ -7,4 +7,4 @@ debian-8/amd64: b16e55fee0967a75aa67a4d859f0455d ubuntu-18.04/amd64: 190b5c45953399fd93354db0621d6f5f debian-10/armhf: 5d92c234d483deaf44c80cad168a8d5d debian-2025.4/amd64: 977965179baa6fd24d0975a05d583eb6 -kali-rolling/amd64: e76880f8ee0608d4220eb77e0abddc62 +kali-rolling/amd64: 6d39e6eb74af4b013e5b98175aea29ae diff --git a/RELEASES b/RELEASES index d83a069c..c1b03668 100644 --- a/RELEASES +++ b/RELEASES @@ -7,4 +7,4 @@ debian-8/amd64: 1.0.0-84 ubuntu-18.04/amd64: 1.0.0-99 debian-10/armhf: 1.0.0-102 debian-2025.4/amd64: 1.0.0-172 -kali-rolling/amd64: 1.0.0-261 +kali-rolling/amd64: 1.0.0-262 From d5e09fca913bfccc346164d45bdf6204e547ceea Mon Sep 17 00:00:00 2001 From: janware DevOps Date: Thu, 10 Sep 2026 23:32:02 +0000 Subject: [PATCH 3/5] Release 1.0.0-262@suse-tumbleweed/x86_64 --- HASH | 2 +- RELEASES | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HASH b/HASH index 9c8bcf9b..02caa49c 100644 --- a/HASH +++ b/HASH @@ -1,4 +1,4 @@ -suse-tumbleweed/x86_64: 0ac23bf5e3b8c21775911bc8d7afc9c9 +suse-tumbleweed/x86_64: 6d39e6eb74af4b013e5b98175aea29ae suse-tumbleweed/i586: 86ea923ca3861900dd03d4b1da44f8b6 debian-10/amd64: 47167eb2d7fe2d845a1b12e0702a6e9b suse-42.3/x86_64: bd0e29fda82e38c89d2ed6f11797ce3a diff --git a/RELEASES b/RELEASES index c1b03668..4c9d0084 100644 --- a/RELEASES +++ b/RELEASES @@ -1,4 +1,4 @@ -suse-tumbleweed/x86_64: 1.0.0-260 +suse-tumbleweed/x86_64: 1.0.0-262 suse-tumbleweed/i586: 1.0.0-44 debian-10/amd64: 1.0.0-47 suse-42.3/x86_64: 1.0.0-48 From 8c1e37500b89a945748c3a65cb5333dfb0735417 Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Fri, 11 Sep 2026 10:48:21 +0200 Subject: [PATCH 4/5] lib.version.Dependency: Reject unsupported spec operators 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 --- src/python/jw/pkg/App.py | 5 ++-- src/python/jw/pkg/lib/version/Dependency.py | 8 +++++- test/unit/python/jw/pkg/lib/version/test.py | 30 ++++++++++++++------- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/python/jw/pkg/App.py b/src/python/jw/pkg/App.py index af9b9311..32a4db97 100644 --- a/src/python/jw/pkg/App.py +++ b/src/python/jw/pkg/App.py @@ -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: diff --git a/src/python/jw/pkg/lib/version/Dependency.py b/src/python/jw/pkg/lib/version/Dependency.py index 8ed8ee3b..01c710c8 100644 --- a/src/python/jw/pkg/lib/version/Dependency.py +++ b/src/python/jw/pkg/lib/version/Dependency.py @@ -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('([~=> 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 ' diff --git a/test/unit/python/jw/pkg/lib/version/test.py b/test/unit/python/jw/pkg/lib/version/test.py index 91783ae3..07817f87 100644 --- a/test/unit/python/jw/pkg/lib/version/test.py +++ b/test/unit/python/jw/pkg/lib/version/test.py @@ -40,7 +40,7 @@ assert d.base_name == 'dev' assert d.full_name == 'dev' # Operators with and without whitespace -for op in ['=', '==', '<', '<=', '>', '>=']: +for op in ['=', '<', '<=', '>', '>=']: d = Dependency(f'foo {op} 1.0') assert d.base_name == 'foo' assert d.full_name == 'foo' @@ -135,14 +135,6 @@ d = Dependency('foo = 1.2.3', app.get_version) assert d.constraint_str(as_range = True) == 'foo = 1.2.3' d = Dependency('foo = 1.0-rc1', app.get_version) assert d.constraint_str(as_range = True) == 'foo = 1.0-rc1' -# unimplemented operators raise -d = Dependency('foo == 1.2.3-45', app.get_version) -try: - d.constraint_str(as_range = True) - assert False, 'Should have raised' -except NotImplementedError: - pass - # Default syntax is SEM_VER d = Dependency('foo < 2.0') assert d.constraint_str(untemplated = False) == 'foo < 2.0' @@ -238,6 +230,26 @@ for bad in ['', 'foo =', ' = 1.0']: except Dependency.Error: pass +# Operators outside the supported set are rejected when parsed, +# not folded into the package name or deferred to expansion +for bad in [ + 'foo ~= 1.0', + 'foo~=1.0', + 'foo ~ 1.0', + 'foo != 1.0', + 'foo!=1.0', + 'foo == 1.0', + 'foo==1.0', + 'foo === 1.0', + 'foo << 1.0', + 'foo >> 1.0', +]: + try: + Dependency(bad).full_name + assert False, f'Should have raised for {bad!r}' + except Dependency.Error as e: + assert 'unsupported operator' in str(e) + # str assert str(Dependency('foo-devel >= 1.0')) == 'foo-devel >= 1.0' assert str(Dependency('foo')) == 'foo' From c31dfb4bbff8ddd976e980e48780ab2dd9b56d93 Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Fri, 11 Sep 2026 10:48:21 +0200 Subject: [PATCH 5/5] lib.version.Dependency: Reject unsupported spec operators 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 --- src/python/jw/pkg/App.py | 5 ++-- src/python/jw/pkg/lib/version/Dependency.py | 8 +++++- test/unit/python/jw/pkg/lib/version/test.py | 30 ++++++++++++++------- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/python/jw/pkg/App.py b/src/python/jw/pkg/App.py index af9b9311..32a4db97 100644 --- a/src/python/jw/pkg/App.py +++ b/src/python/jw/pkg/App.py @@ -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: diff --git a/src/python/jw/pkg/lib/version/Dependency.py b/src/python/jw/pkg/lib/version/Dependency.py index 8ed8ee3b..01c710c8 100644 --- a/src/python/jw/pkg/lib/version/Dependency.py +++ b/src/python/jw/pkg/lib/version/Dependency.py @@ -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('([~=> 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 ' diff --git a/test/unit/python/jw/pkg/lib/version/test.py b/test/unit/python/jw/pkg/lib/version/test.py index 91783ae3..07817f87 100644 --- a/test/unit/python/jw/pkg/lib/version/test.py +++ b/test/unit/python/jw/pkg/lib/version/test.py @@ -40,7 +40,7 @@ assert d.base_name == 'dev' assert d.full_name == 'dev' # Operators with and without whitespace -for op in ['=', '==', '<', '<=', '>', '>=']: +for op in ['=', '<', '<=', '>', '>=']: d = Dependency(f'foo {op} 1.0') assert d.base_name == 'foo' assert d.full_name == 'foo' @@ -135,14 +135,6 @@ d = Dependency('foo = 1.2.3', app.get_version) assert d.constraint_str(as_range = True) == 'foo = 1.2.3' d = Dependency('foo = 1.0-rc1', app.get_version) assert d.constraint_str(as_range = True) == 'foo = 1.0-rc1' -# unimplemented operators raise -d = Dependency('foo == 1.2.3-45', app.get_version) -try: - d.constraint_str(as_range = True) - assert False, 'Should have raised' -except NotImplementedError: - pass - # Default syntax is SEM_VER d = Dependency('foo < 2.0') assert d.constraint_str(untemplated = False) == 'foo < 2.0' @@ -238,6 +230,26 @@ for bad in ['', 'foo =', ' = 1.0']: except Dependency.Error: pass +# Operators outside the supported set are rejected when parsed, +# not folded into the package name or deferred to expansion +for bad in [ + 'foo ~= 1.0', + 'foo~=1.0', + 'foo ~ 1.0', + 'foo != 1.0', + 'foo!=1.0', + 'foo == 1.0', + 'foo==1.0', + 'foo === 1.0', + 'foo << 1.0', + 'foo >> 1.0', +]: + try: + Dependency(bad).full_name + assert False, f'Should have raised for {bad!r}' + except Dependency.Error as e: + assert 'unsupported operator' in str(e) + # str assert str(Dependency('foo-devel >= 1.0')) == 'foo-devel >= 1.0' assert str(Dependency('foo')) == 'foo'