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-07-26 11:30:03
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-cyclopts (Old)
and /work/SRC/openSUSE:Factory/.python-cyclopts.new.2004 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-cyclopts"
Sun Jul 26 11:30:03 2026 rev:7 rq:1367772 version:4.22.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-cyclopts/python-cyclopts.changes
2026-07-22 19:08:37.732299208 +0200
+++
/work/SRC/openSUSE:Factory/.python-cyclopts.new.2004/python-cyclopts.changes
2026-07-26 11:32:40.171384865 +0200
@@ -1,0 +2,11 @@
+Sat Jul 25 19:01:36 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to version 4.22.2:
+ * Fix spurious MissingArgumentError for explicitly-supplied
+ empty mappings
+ * Honor an attached =value on Parameter.count=True flags
+ instead of silently dropping it
+ * Report a clean error when a config node expected to be a
+ table is not one
+
+-------------------------------------------------------------------
Old:
----
cyclopts-4.22.1.tar.gz
New:
----
cyclopts-4.22.2.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-cyclopts.spec ++++++
--- /var/tmp/diff_new_pack.Imcunn/_old 2026-07-26 11:32:40.743404573 +0200
+++ /var/tmp/diff_new_pack.Imcunn/_new 2026-07-26 11:32:40.747404711 +0200
@@ -17,7 +17,7 @@
Name: python-cyclopts
-Version: 4.22.1
+Version: 4.22.2
Release: 0
Summary: Intuitive, easy CLIs based on python type hints
License: Apache-2.0
++++++ cyclopts-4.22.1.tar.gz -> cyclopts-4.22.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cyclopts-4.22.1/PKG-INFO new/cyclopts-4.22.2/PKG-INFO
--- old/cyclopts-4.22.1/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
+++ new/cyclopts-4.22.2/PKG-INFO 2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: cyclopts
-Version: 4.22.1
+Version: 4.22.2
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.22.1/cyclopts/_version.py
new/cyclopts-4.22.2/cyclopts/_version.py
--- old/cyclopts-4.22.1/cyclopts/_version.py 2020-02-02 01:00:00.000000000
+0100
+++ new/cyclopts-4.22.2/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.22.1'
-__version_tuple__ = version_tuple = (4, 22, 1)
+__version__ = version = '4.22.2'
+__version_tuple__ = version_tuple = (4, 22, 2)
__commit_id__ = commit_id = None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cyclopts-4.22.1/cyclopts/argument/_argument.py
new/cyclopts-4.22.2/cyclopts/argument/_argument.py
--- old/cyclopts-4.22.1/cyclopts/argument/_argument.py 2020-02-02
01:00:00.000000000 +0100
+++ new/cyclopts-4.22.2/cyclopts/argument/_argument.py 2020-02-02
01:00:00.000000000 +0100
@@ -626,6 +626,17 @@
if not self.parse:
raise ValueError
+ if self.parameter.count and self.tokens:
+ # An explicit ``=value`` (marked by a non-empty ``value``) sets
the count
+ # and must be the flag's only occurrence; plain repeats may still
sum.
+ explicit = next((x for x in (token, *self.tokens) if x.value and
x.implicit_value is not UNSET), None)
+ if explicit is not None:
+ raise RepeatArgumentError(
+ token=token,
+ msg=f'"{explicit.keyword}={explicit.value}" sets the count
explicitly '
+ "and cannot be combined with other occurrences of the
flag.",
+ )
+
if any(x.address == token.address for x in self.tokens):
if self.parameter.allow_repeating is False:
raise RepeatArgumentError(token=token)
@@ -775,6 +786,9 @@
else:
data = {}
out = UNSET
+ # An explicitly-supplied empty mapping (e.g. ``x = {}`` in a
config file, or
+ # ``--x={}`` on the cli) means "instantiate from defaults", not
"missing".
+ explicit_empty_mapping = False
if self._enum_flag_type:
out = self._enum_flag_type(0)
@@ -784,26 +798,36 @@
out |= reduce(operator.or_, converted_flags) if
isinstance(converted_flags, list) else converted_flags
if self._should_attempt_json_dict():
- while self.tokens:
- token = self.tokens.pop(0)
+ json_tokens, self.tokens = self.tokens, []
+ for token in json_tokens:
try:
parsed_json = json.loads(token.value)
except json.JSONDecodeError as e:
raise CoercionError(token=token,
target_type=self.hint) from e
_validate_json_extra_keys(parsed_json, self.hint, token)
- update_argument_collection(
- {self.name.lstrip("-"): parsed_json},
- token.source,
- self.children_recursive,
- root_keys=(),
- allow_unknown=False,
- )
+ if parsed_json:
+ update_argument_collection(
+ {self.name.lstrip("-"): parsed_json},
+ token.source,
+ self.children_recursive,
+ root_keys=(),
+ allow_unknown=False,
+ )
+ else:
+ explicit_empty_mapping = True
+ # Placeholder so ``has_tokens`` still reports this
argument as supplied.
+ self.tokens.append(token.evolve(value="",
implicit_value={}))
if self._use_pydantic_type_adapter:
return self._convert_pydantic()
if self.tokens and not self._enum_flag_type:
positional_tokens = [token for token in self.tokens if not
token.keys]
+ if any(isinstance(token.implicit_value, dict) for token in
positional_tokens):
+ explicit_empty_mapping = True
+ positional_tokens = [
+ token for token in positional_tokens if not
isinstance(token.implicit_value, dict)
+ ]
if positional_tokens:
return safe_converter(self.hint, tuple(positional_tokens))
@@ -838,7 +862,7 @@
out |= enum_flag_from_dict(self._enum_flag_type, data,
self.parameter.name_transform)
if not out:
out = UNSET
- elif data:
+ elif data or explicit_empty_mapping:
target_hint = self.hint
if self._union_branches:
# ``instantiate_from_dict`` cannot build a bare ``Union``;
pick the branch
@@ -1189,6 +1213,10 @@
out = {}
if self._accepts_keywords:
for token in self.tokens:
+ if not token.keys and isinstance(token.implicit_value, dict):
+ # An explicitly-supplied empty mapping (e.g. ``x = {}`` in
a config file);
+ # contributes no keys, but ``has_tokens`` already marks
the argument as supplied.
+ continue
node = out
for key in token.keys[:-1]:
node = node.setdefault(key, {})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cyclopts-4.22.1/cyclopts/argument/utils.py
new/cyclopts-4.22.2/cyclopts/argument/utils.py
--- old/cyclopts-4.22.1/cyclopts/argument/utils.py 2020-02-02
01:00:00.000000000 +0100
+++ new/cyclopts-4.22.2/cyclopts/argument/utils.py 2020-02-02
01:00:00.000000000 +0100
@@ -379,7 +379,7 @@
if parent_keys is None:
parent_keys = ()
- if isinstance(d, dict):
+ if isinstance(d, dict) and d:
for key, value in d.items():
current_keys = parent_keys + (key,)
if isinstance(value, dict):
@@ -387,7 +387,9 @@
else:
yield current_keys, value
else:
- yield (), d
+ # An empty dict is a leaf: an explicitly-supplied empty mapping (e.g.
``x = {}``)
+ # must produce a token, like an empty list does.
+ yield parent_keys, d
def to_cli_option_name(*keys: str) -> str:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cyclopts-4.22.1/cyclopts/bind.py
new/cyclopts-4.22.2/cyclopts/bind.py
--- old/cyclopts-4.22.1/cyclopts/bind.py 2020-02-02 01:00:00.000000000
+0100
+++ new/cyclopts-4.22.2/cyclopts/bind.py 2020-02-02 01:00:00.000000000
+0100
@@ -225,7 +225,34 @@
cli_values.append(attached_value)
if match.argument.parameter.count:
- match.argument.append(CliToken(keyword=match.matched_token,
implicit_value=1))
+ if cli_values:
+ # An attached ``=value`` (e.g. --verbose=3) explicitly
sets the count.
+ # Plain ``int()`` parse: a count is a number of
occurrences, so
+ # fractional values like "1.5" are rejected rather than
rounded
+ # (unlike general int coercion via ``_int``), as are
negatives.
+ # The non-empty ``value`` marks the token as an explicit
+ # assignment, which ``Argument.append`` forbids combining
with
+ # other occurrences.
+ value = cli_values[-1]
+ try:
+ count_value = int(value)
+ except ValueError:
+ raise CoercionError(
+ token=CliToken(keyword=match.matched_token,
value=value),
+ argument=match.argument,
+ target_type=int,
+ ) from None
+ if count_value < 0:
+ raise CoercionError(
+ token=CliToken(keyword=match.matched_token,
value=value),
+ argument=match.argument,
+ msg="Count values must be non-negative.",
+ )
+ match.argument.append(
+ CliToken(keyword=match.matched_token, value=value,
implicit_value=count_value)
+ )
+ else:
+
match.argument.append(CliToken(keyword=match.matched_token, implicit_value=1))
elif match.implicit_value is not UNSET:
# A flag was parsed
if cli_values:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cyclopts-4.22.1/cyclopts/config/_common.py
new/cyclopts-4.22.2/cyclopts/config/_common.py
--- old/cyclopts-4.22.1/cyclopts/config/_common.py 2020-02-02
01:00:00.000000000 +0100
+++ new/cyclopts-4.22.2/cyclopts/config/_common.py 2020-02-02
01:00:00.000000000 +0100
@@ -54,11 +54,19 @@
arguments: ArgumentCollection,
):
config: dict[str, Any] = self.config.copy()
- try:
- for key in chain(self.root_keys, commands if
self.use_commands_as_keys else ()):
+ traversed: list[str] = []
+ for key in chain(self.root_keys, commands if self.use_commands_as_keys
else ()):
+ try:
config = config[key]
- except KeyError:
- return
+ except KeyError:
+ return
+ traversed.append(key)
+ if not isinstance(config, dict):
+ keyword = "".join(f"[{k}]" for k in traversed)
+ raise CycloptsError(
+ msg=f'Configuration key {keyword} in "{self.source}" must
be a mapping, '
+ f"but got {type(config).__name__}."
+ )
# Hierarchical config uses current app; flat config uses root app to
filter sibling commands
if self.use_commands_as_keys:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cyclopts-4.22.1/cyclopts/exceptions.py
new/cyclopts-4.22.2/cyclopts/exceptions.py
--- old/cyclopts-4.22.1/cyclopts/exceptions.py 2020-02-02 01:00:00.000000000
+0100
+++ new/cyclopts-4.22.2/cyclopts/exceptions.py 2020-02-02 01:00:00.000000000
+0100
@@ -639,6 +639,9 @@
"""The repeated token."""
def _segments(self) -> "Iterator[tuple[str, str] | Text]":
+ if self.msg is not None:
+ yield self._resolved_msg()
+ return
# Invariant: positional duplication is routed to UnusedCliTokensError
by the binder,
# so any token reaching this error path was matched by keyword.
assert self.token.keyword is not None