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