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

Reply via email to