Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-voluptuous for 
openSUSE:Factory checked in at 2026-03-10 17:56:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-voluptuous (Old)
 and      /work/SRC/openSUSE:Factory/.python-voluptuous.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-voluptuous"

Tue Mar 10 17:56:57 2026 rev:13 rq:1337915 version:0.16.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-voluptuous/python-voluptuous.changes      
2024-09-09 14:45:44.305929311 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-voluptuous.new.8177/python-voluptuous.changes
    2026-03-10 18:01:29.119770906 +0100
@@ -1,0 +2,9 @@
+Tue Mar 10 08:37:09 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 0.16.0:
+  * #534: Support requiring `anyOf` a list of keys
+  * #523: Allow Generators for `vol.In`
+  * #524: Fix bug with `Any` validator and `REMOVE_EXTRA`
+  * #530: Add comprehensive tests for `humanize.py` module
+
+-------------------------------------------------------------------

Old:
----
  python-voluptuous-0.15.2-gh.tar.gz

New:
----
  python-voluptuous-0.16.0-gh.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-voluptuous.spec ++++++
--- /var/tmp/diff_new_pack.NGWexJ/_old  2026-03-10 18:01:29.791798535 +0100
+++ /var/tmp/diff_new_pack.NGWexJ/_new  2026-03-10 18:01:29.791798535 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-voluptuous
 #
-# Copyright (c) 2024 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-voluptuous
-Version:        0.15.2
+Version:        0.16.0
 Release:        0
 Summary:        A Python data validation library
 License:        BSD-3-Clause

++++++ python-voluptuous-0.15.2-gh.tar.gz -> python-voluptuous-0.16.0-gh.tar.gz 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/voluptuous-0.15.2/.github/workflows/tests.yml 
new/voluptuous-0.16.0/.github/workflows/tests.yml
--- old/voluptuous-0.15.2/.github/workflows/tests.yml   2024-07-02 
21:06:37.000000000 +0200
+++ new/voluptuous-0.16.0/.github/workflows/tests.yml   2025-12-18 
20:00:01.000000000 +0100
@@ -25,10 +25,10 @@
 
     steps:
       - name: Check out the repository
-        uses: actions/checkout@v3
+        uses: actions/checkout@v5
 
       - name: Set up Python ${{ matrix.python-version }}
-        uses: actions/setup-python@v3
+        uses: actions/setup-python@v5
         with:
           python-version: ${{ matrix.python-version }}
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/voluptuous-0.15.2/CHANGELOG.md 
new/voluptuous-0.16.0/CHANGELOG.md
--- old/voluptuous-0.15.2/CHANGELOG.md  2024-07-02 21:06:37.000000000 +0200
+++ new/voluptuous-0.16.0/CHANGELOG.md  2025-12-18 20:00:01.000000000 +0100
@@ -1,21 +1,36 @@
 # Changelog
 
+## [0.16.0]
+
+**New**:
+
+* [#534](https://github.com/alecthomas/voluptuous/pull/534): Support requiring 
`anyOf` a list of keys
+
+**Fixes**:
+
+* [#523](https://github.com/alecthomas/voluptuous/pull/523): Allow Generators 
for `vol.In`
+* [#524](https://github.com/alecthomas/voluptuous/pull/524): Fix bug with 
`Any` validator and `REMOVE_EXTRA`
+  
+**Changes**:
+
+* [#530](https://github.com/alecthomas/voluptuous/pull/530): Add comprehensive 
tests for `humanize.py` module
+
 ## [0.15.2]
 
 **Fixes**:
 
-* [522](https://github.com/alecthomas/voluptuous/pull/522) Fix regression with 
ALLOW_EXTRA and `Any` validator
+* [522](https://github.com/alecthomas/voluptuous/pull/522): Fix regression 
with ALLOW_EXTRA and `Any` validator
 
 ## [0.15.1]
 
 **Fixes**:
 
-* [515](https://github.com/alecthomas/voluptuous/pull/515) Fix `Remove` not 
removing keys that do not validate
-* [516](https://github.com/alecthomas/voluptuous/pull/516) Improve validator 
typing to allow non-number formats for min and max
-* [517](https://github.com/alecthomas/voluptuous/pull/517) Remove `Maybe` 
validator typing
-* [518](https://github.com/alecthomas/voluptuous/pull/518) Use 
typing.Container for `In` validator
-* [519](https://github.com/alecthomas/voluptuous/pull/519) Don't enforce type 
for unused description attribute
-* [521](https://github.com/alecthomas/voluptuous/pull/521) Type schema 
attribute as `Any`
+* [515](https://github.com/alecthomas/voluptuous/pull/515): Fix `Remove` not 
removing keys that do not validate
+* [516](https://github.com/alecthomas/voluptuous/pull/516): Improve validator 
typing to allow non-number formats for min and max
+* [517](https://github.com/alecthomas/voluptuous/pull/517): Remove `Maybe` 
validator typing
+* [518](https://github.com/alecthomas/voluptuous/pull/518): Use 
typing.Container for `In` validator
+* [519](https://github.com/alecthomas/voluptuous/pull/519): Don't enforce type 
for unused description attribute
+* [521](https://github.com/alecthomas/voluptuous/pull/521): Type schema 
attribute as `Any`
 
 ## [0.15.0]
 
@@ -58,7 +73,6 @@
 * [#470](https://github.com/alecthomas/voluptuous/pull/470): Fix a few code 
comment typos
 * [#472](https://github.com/alecthomas/voluptuous/pull/472): Change to SPDX 
conform license string
 
-
 **New**:
 * [#475](https://github.com/alecthomas/voluptuous/pull/475): Add typing 
information
 * [#478](https://github.com/alecthomas/voluptuous/pull/478): Fix new type hint 
of schemas, for example for `Required('key')`
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/voluptuous-0.15.2/voluptuous/__init__.py 
new/voluptuous-0.16.0/voluptuous/__init__.py
--- old/voluptuous-0.15.2/voluptuous/__init__.py        2024-07-02 
21:06:37.000000000 +0200
+++ new/voluptuous-0.16.0/voluptuous/__init__.py        2025-12-18 
20:00:01.000000000 +0100
@@ -84,5 +84,5 @@
 
 # fmt: on
 
-__version__ = '0.15.2'
+__version__ = '0.16.0'
 __author__ = 'alecthomas'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/voluptuous-0.15.2/voluptuous/schema_builder.py 
new/voluptuous-0.16.0/voluptuous/schema_builder.py
--- old/voluptuous-0.15.2/voluptuous/schema_builder.py  2024-07-02 
21:06:37.000000000 +0200
+++ new/voluptuous-0.16.0/voluptuous/schema_builder.py  2025-12-18 
20:00:01.000000000 +0100
@@ -248,6 +248,13 @@
             )
         )
 
+        # Complex required keys that need special validation
+        complex_required_keys = set(
+            key
+            for key in all_required_keys
+            if isinstance(key, Required) and key.is_complex_key
+        )
+
         # Keys that may have defaults
         all_default_keys = set(
             key
@@ -300,6 +307,22 @@
                     key_value_map[key.schema] = key.default()
 
             errors = []
+
+            # Check complex required keys - at least one candidate key must be 
present
+            for complex_key in complex_required_keys:
+                if not any(
+                    candidate in key_value_map
+                    for candidate in complex_key.candidate_keys
+                ):
+                    msg = (
+                        complex_key.msg
+                        if hasattr(complex_key, 'msg') and complex_key.msg
+                        else f'at least one of {complex_key.candidate_keys} is 
required'
+                    )
+                    errors.append(er.RequiredFieldInvalid(msg, path + 
[complex_key]))
+                else:
+                    # If at least one candidate key is present, mark this 
complex requirement as satisfied
+                    required_keys.discard(complex_key)
             for key, value in key_value_map.items():
                 key_path = path + [key]
                 remove_key = False
@@ -364,11 +387,13 @@
                         continue
                     elif self.extra == ALLOW_EXTRA:
                         out[key] = value
+                    elif self.extra == REMOVE_EXTRA:
+                        #  ignore the key so it's removed from output
+                        continue
                     elif error:
                         errors.append(error)
-                    elif self.extra != REMOVE_EXTRA:
+                    else:
                         errors.append(er.Invalid('extra keys not allowed', 
key_path))
-                        # else REMOVE_EXTRA: ignore the key so it's removed 
from output
 
             # for any required keys left that weren't found and don't have 
defaults:
             for key in required_keys:
@@ -1140,6 +1165,17 @@
     >>> schema = Schema({Required('key', default=list): list})
     >>> schema({})
     {'key': []}
+
+    Complex key validation - at least one of the specified keys must be 
present:
+
+    >>> from voluptuous.validators import Any
+    >>> schema = Schema({Required(Any('color', 'temperature', 'brightness')): 
str})
+    >>> schema({'color': 'red'})  # Valid - has color
+    {'color': 'red'}
+    >>> schema({'temperature': 'warm'})  # Valid - has temperature
+    {'temperature': 'warm'}
+    >>> schema({'color': 'blue', 'brightness': 'high'})  # Valid - has multiple
+    {'color': 'blue', 'brightness': 'high'}
     """
 
     def __init__(
@@ -1151,6 +1187,31 @@
     ) -> None:
         super(Required, self).__init__(schema, msg=msg, 
description=description)
         self.default = default_factory(default)
+        self.is_complex_key = self._is_complex_key_validator(schema)
+        self.candidate_keys = (
+            self._extract_candidate_keys(schema) if self.is_complex_key else 
None
+        )
+
+    def _is_complex_key_validator(self, schema):
+        """Check if schema is a validator that can match multiple keys."""
+        # Import here to avoid circular imports
+        from voluptuous.validators import Any
+
+        return isinstance(schema, Any)
+
+    def _extract_candidate_keys(self, schema):
+        """Extract possible keys from validators like Any("key1", "key2", 
"key3")."""
+        # Import here to avoid circular imports
+        from voluptuous.validators import Any
+
+        if isinstance(schema, Any):
+            # Extract literal values (strings, ints, etc.) from Any validators
+            return [
+                v
+                for v in schema.validators
+                if isinstance(v, (str, int, float, bool, type(None)))
+            ]
+        return []
 
 
 class Remove(Marker):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/voluptuous-0.15.2/voluptuous/tests/tests.py 
new/voluptuous-0.16.0/voluptuous/tests/tests.py
--- old/voluptuous-0.15.2/voluptuous/tests/tests.py     2024-07-02 
21:06:37.000000000 +0200
+++ new/voluptuous-0.16.0/voluptuous/tests/tests.py     2025-12-18 
20:00:01.000000000 +0100
@@ -8,13 +8,60 @@
 import pytest
 
 from voluptuous import (
-    ALLOW_EXTRA, PREVENT_EXTRA, All, AllInvalid, Any, Clamp, Coerce, Contains,
-    ContainsInvalid, Date, Datetime, Email, EmailInvalid, Equal, ExactSequence,
-    Exclusive, Extra, FqdnUrl, In, Inclusive, InInvalid, Invalid, IsDir, 
IsFile, Length,
-    Literal, LiteralInvalid, Marker, Match, MatchInvalid, Maybe, 
MultipleInvalid, NotIn,
-    NotInInvalid, Number, Object, Optional, PathExists, Range, Remove, Replace,
-    Required, Schema, Self, SomeOf, TooManyValid, TypeInvalid, Union, 
Unordered, Url,
-    UrlInvalid, raises, validate,
+    ALLOW_EXTRA,
+    PREVENT_EXTRA,
+    REMOVE_EXTRA,
+    All,
+    AllInvalid,
+    Any,
+    Clamp,
+    Coerce,
+    Contains,
+    ContainsInvalid,
+    Date,
+    Datetime,
+    Email,
+    EmailInvalid,
+    Equal,
+    ExactSequence,
+    Exclusive,
+    Extra,
+    FqdnUrl,
+    In,
+    Inclusive,
+    InInvalid,
+    Invalid,
+    IsDir,
+    IsFile,
+    Length,
+    Literal,
+    LiteralInvalid,
+    Marker,
+    Match,
+    MatchInvalid,
+    Maybe,
+    MultipleInvalid,
+    NotIn,
+    NotInInvalid,
+    Number,
+    Object,
+    Optional,
+    PathExists,
+    Range,
+    Remove,
+    Replace,
+    Required,
+    Schema,
+    Self,
+    SomeOf,
+    TooManyValid,
+    TypeInvalid,
+    Union,
+    Unordered,
+    Url,
+    UrlInvalid,
+    raises,
+    validate,
 )
 from voluptuous.humanize import humanize_error
 from voluptuous.util import Capitalize, Lower, Strip, Title, Upper
@@ -1704,7 +1751,7 @@
     assert str(ctx.value.errors[1]) == "expecting a number @ data['four']"
 
 
-def test_key3():
+def test_any_with_extra_allow():
     schema = Schema(
         {
             Any("name", "area"): str,
@@ -1712,7 +1759,32 @@
         },
         extra=ALLOW_EXTRA,
     )
-    schema(
+
+    result = schema(
+        {
+            "name": "one",
+            "domain": "two",
+            "additional_key": "extra",
+        }
+    )
+
+    assert result == {
+        "name": "one",
+        "domain": "two",
+        "additional_key": "extra",
+    }
+
+
+def test_any_with_extra_remove():
+    schema = Schema(
+        {
+            Any("name", "area"): str,
+            "domain": str,
+        },
+        extra=REMOVE_EXTRA,
+    )
+
+    result = schema(
         {
             "name": "one",
             "domain": "two",
@@ -1720,6 +1792,54 @@
         }
     )
 
+    assert result == {
+        "name": "one",
+        "domain": "two",
+    }
+
+
+def test_any_with_extra_prevent():
+    schema = Schema(
+        {
+            Any("name", "area"): str,
+            "domain": str,
+        },
+        extra=PREVENT_EXTRA,
+    )
+
+    with pytest.raises(MultipleInvalid) as ctx:
+        schema(
+            {
+                "name": "one",
+                "domain": "two",
+                "additional_key": "extra",
+            }
+        )
+
+    assert len(ctx.value.errors) == 1
+    assert str(ctx.value.errors[0]) == "not a valid value @ 
data['additional_key']"
+
+
+def test_any_with_extra_none():
+    schema = Schema(
+        {
+            Any("name", "area"): str,
+            "domain": str,
+        },
+    )
+
+    with pytest.raises(MultipleInvalid) as ctx:
+        schema(
+            {
+                "name": "one",
+                "domain": "two",
+                "additional_key": "extra",
+            }
+        )
+
+    assert len(ctx.value.errors) == 1
+    assert str(ctx.value.errors[0]) == "not a valid value @ 
data['additional_key']"
+
 
 def test_coerce_enum():
     """Test Coerce Enum"""
@@ -1773,3 +1893,284 @@
     assert str(ctx.value.errors) == f"[{invalid_scalar_excp_repr}]"
     ctx.value.add("Test Error")
     assert str(ctx.value.errors) == f"[{invalid_scalar_excp_repr}, 'Test 
Error']"
+
+
+# Additional tests for humanize.py module to improve coverage
+def test_humanize_error_with_nested_getitem_keyerror():
+    """Test _nested_getitem with KeyError (line 19-22)."""
+    from voluptuous.humanize import _nested_getitem
+
+    # Test KeyError handling
+    data = {'a': {'b': 1}}
+    path = ['a', 'c']  # 'c' doesn't exist in {'b': 1}
+    result = _nested_getitem(data, path)
+    assert result is None
+
+
+def test_humanize_error_with_nested_getitem_indexerror():
+    """Test _nested_getitem with IndexError (line 19-22)."""
+    from voluptuous.humanize import _nested_getitem
+
+    # Test IndexError handling
+    data = {'a': [1, 2, 3]}
+    path = ['a', 5]  # Index 5 doesn't exist in [1, 2, 3]
+    result = _nested_getitem(data, path)
+    assert result is None
+
+
+def test_humanize_error_with_nested_getitem_typeerror():
+    """Test _nested_getitem with TypeError (line 19-22)."""
+    from voluptuous.humanize import _nested_getitem
+
+    # Test TypeError handling - data is not subscriptable
+    data = 42  # int is not subscriptable
+    path = ['a']
+    result = _nested_getitem(data, path)
+    assert result is None
+
+
+def test_humanize_error_with_long_error_message():
+    """Test humanize_error with long error message that gets truncated (line 
45)."""
+    from voluptuous.humanize import MAX_VALIDATION_ERROR_ITEM_LENGTH, 
humanize_error
+
+    # Create a very long string that will be truncated
+    long_string = "x" * (MAX_VALIDATION_ERROR_ITEM_LENGTH + 10)
+    data = {'a': long_string}
+    schema = Schema({'a': int})
+
+    with pytest.raises(MultipleInvalid) as ctx:
+        schema(data)
+
+    error_message = humanize_error(data, ctx.value, max_sub_error_length=50)
+    assert "..." in error_message
+    assert len(error_message.split("Got ")[1]) <= 53  # 50 + 3 for "..."
+
+
+def test_validate_with_humanized_errors_success():
+    """Test validate_with_humanized_errors with successful validation (line 
54-57)."""
+    from voluptuous.humanize import validate_with_humanized_errors
+
+    schema = Schema({'a': int, 'b': str})
+    data = {'a': 42, 'b': 'hello'}
+
+    result = validate_with_humanized_errors(data, schema)
+    assert result == data
+
+
+def test_validate_with_humanized_errors_failure():
+    """Test validate_with_humanized_errors with validation failure (line 
54-57)."""
+    from voluptuous.humanize import Error, validate_with_humanized_errors
+
+    schema = Schema({'a': int, 'b': str})
+    data = {'a': 'not an int', 'b': 123}
+
+    with pytest.raises(Error) as ctx:
+        validate_with_humanized_errors(data, schema)
+
+    error_message = str(ctx.value)
+    assert "expected int for dictionary value @ data['a']" in error_message
+    assert "expected str for dictionary value @ data['b']" in error_message
+    assert "Got 'not an int'" in error_message
+    assert "Got 123" in error_message
+
+
+def test_validate_with_humanized_errors_custom_max_length():
+    """Test validate_with_humanized_errors with custom max_sub_error_length."""
+    from voluptuous.humanize import Error, validate_with_humanized_errors
+
+    schema = Schema({'a': int})
+    data = {'a': 'not an int'}
+
+    with pytest.raises(Error) as ctx:
+        validate_with_humanized_errors(data, schema, max_sub_error_length=10)
+
+    error_message = str(ctx.value)
+    assert "..." in error_message  # Should be truncated
+
+
+def test_humanize_error_with_multiple_invalid():
+    """Test humanize_error with MultipleInvalid containing multiple errors."""
+    from voluptuous.humanize import humanize_error
+
+    schema = Schema({'a': int, 'b': str, 'c': [int]})
+    data = {'a': 'not an int', 'b': 123, 'c': ['not an int']}
+
+    with pytest.raises(MultipleInvalid) as ctx:
+        schema(data)
+
+    error_message = humanize_error(data, ctx.value)
+    # Should contain all three error messages
+    assert "expected int for dictionary value @ data['a']" in error_message
+    assert "expected str for dictionary value @ data['b']" in error_message
+    assert "expected int @ data['c'][0]" in error_message
+
+
+def test_humanize_error_with_single_invalid():
+    """Test humanize_error with single Invalid error."""
+    from voluptuous.humanize import humanize_error
+
+    schema = Schema({'a': int})
+    data = {'a': 'not an int'}
+
+    with pytest.raises(MultipleInvalid) as ctx:
+        schema(data)
+
+    error_message = humanize_error(data, ctx.value)
+    assert "expected int for dictionary value @ data['a']" in error_message
+    assert "Got 'not an int'" in error_message
+
+
+def test_humanize_error_with_none_data():
+    """Test humanize_error with None data."""
+    from voluptuous.humanize import _nested_getitem, humanize_error
+
+    # Test _nested_getitem with None data
+    result = _nested_getitem(None, ['a'])
+    assert result is None
+
+    # Test humanize_error with None data
+    schema = Schema({'a': int})
+    data = None
+
+    with pytest.raises(MultipleInvalid) as ctx:
+        schema(data)
+
+    error_message = humanize_error(data, ctx.value)
+    assert "expected a dictionary" in error_message
+
+
+def test_required_complex_key_any():
+    """Test Required with Any validator for multiple possible keys"""
+    schema = Schema(
+        {Required(Any("color", "temperature", "brightness")): str, 
"device_id": str}
+    )
+
+    # Should pass - defines one of the required keys
+    result = schema({"color": "red", "device_id": "light1"})
+    assert result == {"color": "red", "device_id": "light1"}
+
+    # Should pass - defines several of the required keys
+    result = schema({"color": "blue", "brightness": "50%", "device_id": 
"light1"})
+    assert result == {"color": "blue", "brightness": "50%", "device_id": 
"light1"}
+
+    # Should fail - has none of the required keys
+    with pytest.raises(MultipleInvalid) as ctx:
+        schema({"device_id": "light1"})
+
+    error_msg = str(ctx.value)
+    assert (
+        "at least one of ['color', 'temperature', 'brightness'] is required"
+        in error_msg
+    )
+
+
+def test_required_complex_key_custom_message():
+    """Test Required with Any validator and custom error message"""
+    schema = Schema(
+        {
+            Required(
+                Any("color", "temperature", "brightness"),
+                msg="Please specify a lighting attribute",
+            ): str,
+            "device_id": str,
+        }
+    )
+
+    # Should pass
+    schema({"color": "red", "device_id": "light1"})
+
+    # Should fail with custom message
+    with pytest.raises(MultipleInvalid) as ctx:
+        schema({"device_id": "light1"})
+
+    error_msg = str(ctx.value)
+    assert "Please specify a lighting attribute" in error_msg
+
+
+def test_required_complex_key_mixed_types():
+    """Test Required with Any validator containing mixed key types"""
+    schema = Schema({Required(Any("string_key", 123, 45.6)): str, "other": 
int})
+
+    # Should work with string key
+    result = schema({"string_key": "value", "other": 1})
+    assert result == {"string_key": "value", "other": 1}
+
+    # Should work with int key
+    result = schema({123: "value", "other": 1})
+    assert result == {123: "value", "other": 1}
+
+    # Should work with float key
+    result = schema({45.6: "value", "other": 1})
+    assert result == {45.6: "value", "other": 1}
+
+    # Should fail with none present
+    with pytest.raises(MultipleInvalid) as ctx:
+        schema({"other": 1})
+
+    error_msg = str(ctx.value)
+    assert "at least one of ['string_key', 123, 45.6] is required" in error_msg
+
+
+def test_required_complex_key_multiple_complex_requirements():
+    """Test multiple Required complex keys in same schema"""
+    schema = Schema(
+        {
+            Required(Any("color", "hue")): str,
+            Required(Any("brightness", "intensity")): str,
+            "device": str,
+        }
+    )
+
+    # Should pass with one from each group
+    result = schema({"color": "red", "brightness": "high", "device": "light"})
+    assert result == {"color": "red", "brightness": "high", "device": "light"}
+
+    # Should fail if missing on any group
+    with pytest.raises(MultipleInvalid) as ctx:
+        schema({"brightness": "high", "device": "light"})
+
+    error_msg = str(ctx.value)
+    assert "at least one of ['color', 'hue'] is required" in error_msg
+
+
+def test_required_complex_key_value_validation():
+    """Test that value validation still works with complex required keys"""
+    schema = Schema({Required(Any("color", "temperature")): str, "device": 
str})
+
+    # Should pass with valid string value
+    result = schema({"color": "red", "device": "light"})
+    assert result == {"color": "red", "device": "light"}
+
+    # Should fail with invalid value type
+    with pytest.raises(MultipleInvalid) as ctx:
+        schema({"color": 123, "device": "light"})  # color should be str, not 
int
+
+    error_msg = str(ctx.value)
+    assert "expected str" in error_msg
+
+
+def test_complex_required_keys_with_specific_value_validation():
+    """Test complex required keys combined with specific value validation for 
brightness range."""
+    schema = Schema(
+        {
+            Required(Any('color', 'temperature', 'brightness')): object,
+            'brightness': All(
+                Coerce(int), Range(min=0, max=100)
+            ),  # Additional validation for brightness specifically
+            'device_id': str,
+        }
+    )
+
+    # Valid - color provided, no brightness validation needed
+    result = schema({'color': 'red', 'device_id': 'light1'})
+    assert result == {'color': 'red', 'device_id': 'light1'}
+
+    # Invalid - brightness provided but out of range (255 > 100)
+    # Should NOT get "required field missing" error, but should get range error
+    with pytest.raises(MultipleInvalid) as exc_info:
+        schema({'brightness': '255', 'device_id': 'light1'})
+
+    # Verify it's a range error, not a missing required field error
+    error_msg = str(exc_info.value)
+    assert "required" not in error_msg.lower()  # No "required field missing" 
error
+    assert "value must be at most 100" in error_msg  # Range validation error
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/voluptuous-0.15.2/voluptuous/validators.py 
new/voluptuous-0.16.0/voluptuous/validators.py
--- old/voluptuous-0.15.2/voluptuous/validators.py      2024-07-02 
21:06:37.000000000 +0200
+++ new/voluptuous-0.16.0/voluptuous/validators.py      2025-12-18 
20:00:01.000000000 +0100
@@ -10,11 +10,31 @@
 from functools import wraps
 
 from voluptuous.error import (
-    AllInvalid, AnyInvalid, BooleanInvalid, CoerceInvalid, ContainsInvalid, 
DateInvalid,
-    DatetimeInvalid, DirInvalid, EmailInvalid, ExactSequenceInvalid, 
FalseInvalid,
-    FileInvalid, InInvalid, Invalid, LengthInvalid, MatchInvalid, 
MultipleInvalid,
-    NotEnoughValid, NotInInvalid, PathInvalid, RangeInvalid, TooManyValid, 
TrueInvalid,
-    TypeInvalid, UrlInvalid,
+    AllInvalid,
+    AnyInvalid,
+    BooleanInvalid,
+    CoerceInvalid,
+    ContainsInvalid,
+    DateInvalid,
+    DatetimeInvalid,
+    DirInvalid,
+    EmailInvalid,
+    ExactSequenceInvalid,
+    FalseInvalid,
+    FileInvalid,
+    InInvalid,
+    Invalid,
+    LengthInvalid,
+    MatchInvalid,
+    MultipleInvalid,
+    NotEnoughValid,
+    NotInInvalid,
+    PathInvalid,
+    RangeInvalid,
+    TooManyValid,
+    TrueInvalid,
+    TypeInvalid,
+    UrlInvalid,
 )
 
 # F401: flake8 complains about 'raises' not being used, but it is used in 
doctests
@@ -812,7 +832,9 @@
     """Validate that a value is in a collection."""
 
     def __init__(
-        self, container: typing.Container, msg: typing.Optional[str] = None
+        self,
+        container: typing.Container | typing.Iterable,
+        msg: typing.Optional[str] = None,
     ) -> None:
         self.container = container
         self.msg = msg

Reply via email to