Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-cyclopts for openSUSE:Factory
checked in at 2026-08-21 16:53:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-cyclopts (Old)
and /work/SRC/openSUSE:Factory/.python-cyclopts.new.1258 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-cyclopts"
Fri Aug 21 16:53:28 2026 rev:12 rq:1372691 version:4.23.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-cyclopts/python-cyclopts.changes
2026-08-19 18:01:17.943408187 +0200
+++
/work/SRC/openSUSE:Factory/.python-cyclopts.new.1258/python-cyclopts.changes
2026-08-21 16:54:53.764304669 +0200
@@ -1,0 +2,9 @@
+Fri Aug 21 05:35:07 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to 4.23.1:
+ * Honor a dict value type's Parameter annotation
+ * Fix Number, Path, and Slice validators silently skipping
+ set, frozenset, and dict elements
+ * Skip var-keyword parameters in shell completion generation
+
+-------------------------------------------------------------------
Old:
----
cyclopts-4.23.0.tar.gz
New:
----
cyclopts-4.23.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-cyclopts.spec ++++++
--- /var/tmp/diff_new_pack.IURZAM/_old 2026-08-21 16:54:54.479329869 +0200
+++ /var/tmp/diff_new_pack.IURZAM/_new 2026-08-21 16:54:54.481329940 +0200
@@ -17,7 +17,7 @@
Name: python-cyclopts
-Version: 4.23.0
+Version: 4.23.1
Release: 0
Summary: Intuitive, easy CLIs based on python type hints
License: Apache-2.0
++++++ cyclopts-4.23.0.tar.gz -> cyclopts-4.23.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cyclopts-4.23.0/PKG-INFO new/cyclopts-4.23.1/PKG-INFO
--- old/cyclopts-4.23.0/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
+++ new/cyclopts-4.23.1/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.5
Name: cyclopts
-Version: 4.23.0
+Version: 4.23.1
Summary: Intuitive, easy CLIs based on type hints.
Project-URL: Homepage, https://github.com/BrianPugh/cyclopts
Project-URL: Repository, https://github.com/BrianPugh/cyclopts
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cyclopts-4.23.0/cyclopts/_convert.py
new/cyclopts-4.23.1/cyclopts/_convert.py
--- old/cyclopts-4.23.0/cyclopts/_convert.py 2020-02-02 01:00:00.000000000
+0100
+++ new/cyclopts-4.23.1/cyclopts/_convert.py 2020-02-02 01:00:00.000000000
+0100
@@ -11,6 +11,7 @@
from functools import partial, reduce
from typing import (
TYPE_CHECKING,
+ Annotated,
Any,
Literal,
TypeVar,
@@ -868,6 +869,11 @@
convert_priv = partial(_convert, converter=converter,
name_transform=name_transform)
convert_tuple = partial(_convert_tuple, converter=converter,
name_transform=name_transform)
+ # ``_convert`` applies a type's own ``Parameter`` metadata
(converter/validator), but
+ # ``resolve`` discards it. Stash the metadata so it can be re-attached to
the
+ # *normalized* type for the scalar dispatch below; this is what lets a
container's
+ # value type carry its own ``Parameter``, e.g. ``dict[str, PositiveInt]``.
+ annotations_ = get_args(type_)[1:] if is_annotated(type_) else ()
type_ = resolve(type_)
if type_ is Any:
@@ -910,14 +916,15 @@
return convert_enum_flag(maybe_origin_type, tokens, name_transform)
else:
tokens_per_element, consume_all = token_count(type_)
+ annotated_type_ = Annotated[(type_, *annotations_)] if annotations_
else type_
if consume_all:
- return convert_priv(type_, tokens) # pyright: ignore
+ return convert_priv(annotated_type_, tokens) # pyright: ignore
elif len(tokens) == 1:
- return convert_priv(type_, tokens[0]) # pyright: ignore
+ return convert_priv(annotated_type_, tokens[0]) # pyright: ignore
elif tokens_per_element == 1:
- return [convert_priv(type_, item) for item in tokens] # pyright:
ignore
+ return [convert_priv(annotated_type_, item) for item in tokens] #
pyright: ignore
elif len(tokens) == tokens_per_element:
- return convert_priv(type_, tokens) # pyright: ignore
+ return convert_priv(annotated_type_, tokens) # pyright: ignore
else:
raise NotImplementedError("Unreachable?")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cyclopts-4.23.0/cyclopts/_version.py
new/cyclopts-4.23.1/cyclopts/_version.py
--- old/cyclopts-4.23.0/cyclopts/_version.py 2020-02-02 01:00:00.000000000
+0100
+++ new/cyclopts-4.23.1/cyclopts/_version.py 2020-02-02 01:00:00.000000000
+0100
@@ -18,7 +18,7 @@
commit_id: str | None
__commit_id__: str | None
-__version__ = version = '4.23.0'
-__version_tuple__ = version_tuple = (4, 23, 0)
+__version__ = version = '4.23.1'
+__version_tuple__ = version_tuple = (4, 23, 1)
__commit_id__ = commit_id = None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cyclopts-4.23.0/cyclopts/completion/_base.py
new/cyclopts-4.23.1/cyclopts/completion/_base.py
--- old/cyclopts-4.23.0/cyclopts/completion/_base.py 2020-02-02
01:00:00.000000000 +0100
+++ new/cyclopts-4.23.1/cyclopts/completion/_base.py 2020-02-02
01:00:00.000000000 +0100
@@ -15,6 +15,7 @@
from cyclopts.annotations import ITERABLE_TYPES, is_annotated, is_union
from cyclopts.argument import ArgumentCollection
from cyclopts.exceptions import CycloptsError
+from cyclopts.field_info import VAR_KEYWORD
from cyclopts.group_extractors import RegisteredCommand, groups_from_app
from cyclopts.utils import frozen, is_class_and_subclass
@@ -105,6 +106,13 @@
own_arguments = ArgumentCollection()
with app.app_stack(execution_path):
for subapp, app_arguments in
_iter_resolution_argument_collections(execution_path, parse_docstring=True):
+ # ``**kwargs`` accepts arbitrary option names, so no
per-option spec can
+ # represent it; its ``--[KEYWORD]`` placeholder name is
invalid shell
+ # syntax (zsh's ``_arguments`` aborts on it, breaking
completion for the
+ # entire command).
+ app_arguments = ArgumentCollection(
+ argument for argument in app_arguments if
argument.field_info.kind is not VAR_KEYWORD
+ )
arguments.extend(app_arguments)
if not any(subapp is a for a in current):
continue # inherited (ancestor meta launcher)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cyclopts-4.23.0/cyclopts/validators/_number.py
new/cyclopts-4.23.1/cyclopts/validators/_number.py
--- old/cyclopts-4.23.0/cyclopts/validators/_number.py 2020-02-02
01:00:00.000000000 +0100
+++ new/cyclopts-4.23.1/cyclopts/validators/_number.py 2020-02-02
01:00:00.000000000 +0100
@@ -1,13 +1,17 @@
-from collections.abc import Sequence
from typing import Any
from cyclopts.utils import frozen
+from cyclopts.validators._utils import iter_container_elements
@frozen(kw_only=True)
class Number:
"""Limit input number to a value range.
+ If the annotated parameter is a container (``list``, ``tuple``, ``set``,
+ ``frozenset``, or ``dict``), each element is validated individually.
+ For a ``dict``, the **values** are validated; keys are not.
+
Example Usage:
.. code-block:: python
@@ -57,10 +61,9 @@
"""Input value must be a multiple of this value."""
def __call__(self, type_: Any, value: Any):
- if isinstance(value, Sequence):
- if isinstance(value, str):
- raise TypeError
- for v in value:
+ elements = iter_container_elements(value)
+ if elements is not None:
+ for v in elements:
self(type_, v)
else:
if not isinstance(value, int | float):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cyclopts-4.23.0/cyclopts/validators/_path.py
new/cyclopts-4.23.1/cyclopts/validators/_path.py
--- old/cyclopts-4.23.0/cyclopts/validators/_path.py 2020-02-02
01:00:00.000000000 +0100
+++ new/cyclopts-4.23.1/cyclopts/validators/_path.py 2020-02-02
01:00:00.000000000 +0100
@@ -5,6 +5,7 @@
from attrs import field
from cyclopts.utils import frozen, to_tuple_converter
+from cyclopts.validators._utils import iter_container_elements
def ext_converter(value: str | Iterable[str] | None) -> tuple[str, ...]:
@@ -15,6 +16,10 @@
class Path:
"""Assertions on properties of :class:`pathlib.Path`.
+ If the annotated parameter is a container (``list``, ``tuple``, ``set``,
+ ``frozenset``, or ``dict``), each element is validated individually.
+ For a ``dict``, the **values** are validated; keys are not.
+
Example Usage:
.. code-block:: python
@@ -89,11 +94,9 @@
raise ValueError("(exists=True, file_okay=False, dir_okay=False)
is an invalid configuration.")
def __call__(self, type_: Any, path: Any):
- if isinstance(path, Sequence):
- if isinstance(path, str):
- raise TypeError
-
- for p in path:
+ elements = iter_container_elements(path)
+ if elements is not None:
+ for p in elements:
self(type_, p)
else:
if not isinstance(path, pathlib.Path):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cyclopts-4.23.0/cyclopts/validators/_slice.py
new/cyclopts-4.23.1/cyclopts/validators/_slice.py
--- old/cyclopts-4.23.0/cyclopts/validators/_slice.py 2020-02-02
01:00:00.000000000 +0100
+++ new/cyclopts-4.23.1/cyclopts/validators/_slice.py 2020-02-02
01:00:00.000000000 +0100
@@ -1,7 +1,7 @@
-from collections.abc import Sequence
from typing import Any
from cyclopts.utils import frozen
+from cyclopts.validators._utils import iter_container_elements
def _selects_nothing(s: slice) -> bool:
@@ -39,6 +39,10 @@
class Slice:
"""Assertions on properties of a :class:`slice`.
+ If the annotated parameter is a container (``list``, ``tuple``, ``set``,
+ ``frozenset``, or ``dict``), each element is validated individually.
+ For a ``dict``, the **values** are validated; keys are not.
+
Example Usage:
.. code-block:: python
@@ -72,10 +76,9 @@
"""If :obj:`False`, the slice **must** select a non-empty range. Defaults
to :obj:`True`."""
def __call__(self, type_: Any, value: Any):
- if isinstance(value, Sequence):
- if isinstance(value, str):
- raise TypeError
- for v in value:
+ elements = iter_container_elements(value)
+ if elements is not None:
+ for v in elements:
self(type_, v)
else:
if not isinstance(value, slice):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cyclopts-4.23.0/cyclopts/validators/_utils.py
new/cyclopts-4.23.1/cyclopts/validators/_utils.py
--- old/cyclopts-4.23.0/cyclopts/validators/_utils.py 1970-01-01
01:00:00.000000000 +0100
+++ new/cyclopts-4.23.1/cyclopts/validators/_utils.py 2020-02-02
01:00:00.000000000 +0100
@@ -0,0 +1,35 @@
+from collections.abc import Iterator, Mapping, Sequence
+from collections.abc import Set as AbstractSet
+from typing import Any
+
+
+def iter_container_elements(value: Any) -> Iterator[Any] | None:
+ """Yield the elements a per-element validator should recurse into.
+
+ Cyclopts converts container-annotated parameters into concrete ``list``,
+ ``tuple``, ``set``, ``frozenset``, and ``dict`` objects, and hands the
+ **whole** container to the parameter's validator. Validators use this
helper
+ so every container kind recurses uniformly.
+
+ Mappings yield their **values**; a mapping's keys are not validated. This
+ matches how ``**kwargs`` parameters are validated elsewhere in Cyclopts.
+
+ Returns
+ -------
+ Iterator | None
+ An iterator over the elements to validate, or :obj:`None` if ``value``
+ is not a container (i.e. it should be validated as a scalar).
+
+ Raises
+ ------
+ TypeError
+ If ``value`` is a :class:`str`, which is a
:class:`~collections.abc.Sequence`
+ but never a valid container of values to validate.
+ """
+ if isinstance(value, Mapping):
+ return iter(value.values())
+ if isinstance(value, Sequence | AbstractSet):
+ if isinstance(value, str):
+ raise TypeError
+ return iter(value)
+ return None