back to Claude Sonnet 3.5 - Base summary
Claude Sonnet 3.5 - Base: voluptuous
Failed to run pytests for test tests
Pytest collection failure.
Patch diff
diff --git a/voluptuous/error.py b/voluptuous/error.py
index f72fbe7..7999b26 100644
--- a/voluptuous/error.py
+++ b/voluptuous/error.py
@@ -146,7 +146,7 @@ class LiteralInvalid(Invalid):
class LengthInvalid(Invalid):
- pass
+ """The value has an invalid length."""
class DatetimeInvalid(Invalid):
@@ -158,22 +158,28 @@ class DateInvalid(Invalid):
class InInvalid(Invalid):
- pass
+ """The value is not in the required collection."""
class NotInInvalid(Invalid):
- pass
+ """The value is in a collection it should not be in."""
class ExactSequenceInvalid(Invalid):
- pass
+ """The sequence does not match exactly."""
class NotEnoughValid(Invalid):
"""The value did not pass enough validations."""
- pass
+ def __init__(self, msg: str, min_valid: int, actual_valid: int, path: typing.Optional[typing.List[typing.Hashable]] = None):
+ super().__init__(msg, path)
+ self.min_valid = min_valid
+ self.actual_valid = actual_valid
class TooManyValid(Invalid):
"""The value passed more than expected validations."""
- pass
+ def __init__(self, msg: str, max_valid: int, actual_valid: int, path: typing.Optional[typing.List[typing.Hashable]] = None):
+ super().__init__(msg, path)
+ self.max_valid = max_valid
+ self.actual_valid = actual_valid
diff --git a/voluptuous/humanize.py b/voluptuous/humanize.py
index 2902871..7776e89 100644
--- a/voluptuous/humanize.py
+++ b/voluptuous/humanize.py
@@ -11,4 +11,32 @@ def humanize_error(data, validation_error: Invalid, max_sub_error_length:
Invalid and MultipleInvalid do not include the offending value in error messages,
and MultipleInvalid.__str__ only provides the first error.
"""
- pass
+ if isinstance(validation_error, MultipleInvalid):
+ errors = validation_error.errors
+ else:
+ errors = [validation_error]
+
+ error_messages = []
+ for error in errors:
+ path = _format_path(error.path)
+ value = _get_value_from_path(data, error.path)
+ error_message = f"Error at {path}: {error}"
+ if value is not None:
+ truncated_value = str(value)[:max_sub_error_length]
+ if len(str(value)) > max_sub_error_length:
+ truncated_value += "..."
+ error_message += f" (got {truncated_value})"
+ error_messages.append(error_message)
+
+ return "\n".join(error_messages)
+
+def _format_path(path):
+ return ".".join(str(p) for p in path) if path else "root"
+
+def _get_value_from_path(data, path):
+ for key in path:
+ if isinstance(data, (dict, list)) and key in data:
+ data = data[key]
+ else:
+ return None
+ return data
diff --git a/voluptuous/schema_builder.py b/voluptuous/schema_builder.py
index de2b53c..57ef356 100644
--- a/voluptuous/schema_builder.py
+++ b/voluptuous/schema_builder.py
@@ -113,7 +113,27 @@ class Schema(object):
Note: only very basic inference is supported.
"""
- pass
+ def infer_type(value):
+ if isinstance(value, dict):
+ return {k: infer_type(v) for k, v in value.items()}
+ elif isinstance(value, list):
+ if value:
+ return [infer_type(value[0])]
+ else:
+ return list
+ elif isinstance(value, str):
+ return str
+ elif isinstance(value, int):
+ return int
+ elif isinstance(value, float):
+ return float
+ elif isinstance(value, bool):
+ return bool
+ else:
+ return type(value)
+
+ inferred_schema = infer_type(data)
+ return cls(inferred_schema, **kwargs)
def __eq__(self, other):
if not isinstance(other, Schema):
@@ -142,7 +162,58 @@ class Schema(object):
def _compile_mapping(self, schema, invalid_msg=None):
"""Create validator for given mapping."""
- pass
+ def validate_mapping(path, iterable, value):
+ if not isinstance(value, dict):
+ raise er.Invalid(invalid_msg or 'expected a dictionary', path)
+
+ out = {}
+ required_keys = set()
+ optional_keys = set()
+ _compile = self._compile
+ error = er.MultipleInvalid()
+
+ for key, subschema in _iterate_mapping_candidates(schema):
+ if isinstance(key, Required):
+ required_keys.add(key.schema)
+ elif isinstance(key, Optional):
+ optional_keys.add(key.schema)
+ else:
+ optional_keys.add(key)
+
+ for key, val in value.items():
+ key_path = path + [key]
+ for skey, svalue in _iterate_mapping_candidates(schema):
+ if isinstance(skey, Optional):
+ skey = skey.schema
+ if isinstance(skey, Required):
+ skey = skey.schema
+ if skey == key:
+ try:
+ out[key] = _compile(svalue)(key_path, value, val)
+ break
+ except er.Invalid as e:
+ error.add(e)
+ else:
+ if self.extra == PREVENT_EXTRA:
+ error.add(er.Invalid('extra keys not allowed', key_path))
+ elif self.extra == ALLOW_EXTRA:
+ out[key] = val
+ elif self.extra == REMOVE_EXTRA:
+ pass
+ else:
+ raise ValueError('Invalid value for extra')
+
+ missing_required_keys = required_keys - set(out.keys())
+ if missing_required_keys:
+ error.add(er.Invalid(f'required key(s) {", ".join(repr(k) for k in missing_required_keys)} not provided',
+ path))
+
+ if error.errors:
+ raise error
+
+ return out
+
+ return validate_mapping
def _compile_object(self, schema):
"""Validate an object.
@@ -162,7 +233,26 @@ class Schema(object):
... validate(Structure(one='three'))
"""
- pass
+ base = self._compile_mapping(schema, invalid_msg='object value')
+
+ def validate_object(path, iterable, value):
+ if schema.cls is not UNDEFINED and not isinstance(value, schema.cls):
+ raise er.Invalid('expected {} but got {}'.format(schema.cls, type(value)), path)
+
+ # Convert object attributes to a dictionary
+ value_dict = {k: getattr(value, k) for k in dir(value) if not k.startswith('_')}
+
+ # Validate the dictionary
+ result_dict = base(path, iterable, value_dict)
+
+ # Create a new object with validated attributes
+ validated_obj = schema.cls() if schema.cls is not UNDEFINED else type(value)()
+ for k, v in result_dict.items():
+ setattr(validated_obj, k, v)
+
+ return validated_obj
+
+ return validate_object
def _compile_dict(self, schema):
"""Validate a dictionary.
@@ -240,7 +330,7 @@ class Schema(object):
"expected str for dictionary value @ data['adict']['strfield']"]
"""
- pass
+ return self._compile_mapping(schema, invalid_msg='expected a dictionary')
def _compile_sequence(self, schema, seq_type):
"""Validate a sequence type.
@@ -255,7 +345,27 @@ class Schema(object):
>>> validator([1])
[1]
"""
- pass
+ _compile = self._compile
+ seq_schema = [_compile(s) for s in schema]
+
+ def validate_sequence(path, iterable, value):
+ if not isinstance(value, seq_type):
+ raise er.Invalid('expected a {}'.format(seq_type.__name__), path)
+
+ result = []
+ for i, item in enumerate(value):
+ item_path = path + [i]
+ for validator in seq_schema:
+ try:
+ result.append(validator(item_path, iterable, item))
+ break
+ except er.Invalid:
+ pass
+ else:
+ raise er.Invalid('invalid value', item_path)
+ return seq_type(result)
+
+ return validate_sequence
def _compile_tuple(self, schema):
"""Validate a tuple.
@@ -270,7 +380,7 @@ class Schema(object):
>>> validator((1,))
(1,)
"""
- pass
+ return self._compile_sequence(schema, tuple)
def _compile_list(self, schema):
"""Validate a list.
@@ -285,7 +395,7 @@ class Schema(object):
>>> validator([1])
[1]
"""
- pass
+ return self._compile_sequence(schema, list)
def _compile_set(self, schema):
"""Validate a set.
@@ -300,7 +410,26 @@ class Schema(object):
>>> with raises(er.MultipleInvalid, 'invalid value in set'):
... validator(set(['a']))
"""
- pass
+ _compile = self._compile
+ set_schema = [_compile(s) for s in schema]
+
+ def validate_set(path, iterable, value):
+ if not isinstance(value, set):
+ raise er.Invalid('expected a set', path)
+
+ result = set()
+ for item in value:
+ for validator in set_schema:
+ try:
+ result.add(validator(path, iterable, item))
+ break
+ except er.Invalid:
+ pass
+ else:
+ raise er.Invalid('invalid value in set', path)
+ return result
+
+ return validate_set
def extend(self, schema: Schemable, required: typing.Optional[bool]=
None, extra: typing.Optional[int]=None) ->Schema:
@@ -316,7 +445,17 @@ class Schema(object):
:param required: if set, overrides `required` of this `Schema`
:param extra: if set, overrides `extra` of this `Schema`
"""
- pass
+ if not isinstance(self.schema, dict) or not isinstance(schema, dict):
+ raise ValueError("Both schemas must be dictionary-based")
+
+ new_schema = self.schema.copy()
+ new_schema.update(schema)
+
+ return Schema(
+ new_schema,
+ required=self.required if required is None else required,
+ extra=self.extra if extra is None else extra
+ )
def _compile_scalar(schema):
@@ -678,17 +817,31 @@ def message(default: typing.Optional[str]=None, cls: typing.Optional[typing
... except er.MultipleInvalid as e:
... assert isinstance(e.errors[0], IntegerInvalid)
"""
- pass
+ def decorator(func):
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ msg = kwargs.pop('msg', None) or default
+ clsoverride = kwargs.pop('clsoverride', None) or cls
+ try:
+ return func(*args, **kwargs)
+ except ValueError as e:
+ raise (clsoverride or er.Invalid)(msg or str(e))
+ return wrapper
+ return decorator
def _args_to_dict(func, args):
"""Returns argument names as values as key-value pairs."""
- pass
+ arg_count = func.__code__.co_argcount
+ arg_names = func.__code__.co_varnames[:arg_count]
+ return dict(zip(arg_names, args))
def _merge_args_with_kwargs(args_dict, kwargs_dict):
"""Merge args with kwargs."""
- pass
+ ret = args_dict.copy()
+ ret.update(kwargs_dict)
+ return ret
def validate(*a, **kw) ->typing.Callable:
@@ -707,4 +860,20 @@ def validate(*a, **kw) ->typing.Callable:
... return arg1 * 2
"""
- pass
+ def validate_schema_decorator(func):
+ schema = Schema(kw) if kw else Schema(a[0])
+
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ schema_dict = _merge_args_with_kwargs(_args_to_dict(func, args), kwargs)
+ validated = schema(schema_dict)
+ result = func(**validated)
+ if '__return__' in schema.schema:
+ return_validator = schema.schema['__return__']
+ if isinstance(return_validator, Schema):
+ return return_validator(result)
+ else:
+ return Schema(return_validator)(result)
+ return result
+ return wrapper
+ return validate_schema_decorator
diff --git a/voluptuous/util.py b/voluptuous/util.py
index fe15b1a..a2c57d8 100644
--- a/voluptuous/util.py
+++ b/voluptuous/util.py
@@ -13,7 +13,7 @@ def Lower(v: str) ->str:
>>> s('HI')
'hi'
"""
- pass
+ return v.lower()
def Upper(v: str) ->str:
@@ -23,7 +23,7 @@ def Upper(v: str) ->str:
>>> s('hi')
'HI'
"""
- pass
+ return v.upper()
def Capitalize(v: str) ->str:
@@ -33,7 +33,7 @@ def Capitalize(v: str) ->str:
>>> s('hello world')
'Hello world'
"""
- pass
+ return v.capitalize()
def Title(v: str) ->str:
@@ -43,7 +43,7 @@ def Title(v: str) ->str:
>>> s('hello world')
'Hello World'
"""
- pass
+ return v.title()
def Strip(v: str) ->str:
@@ -53,7 +53,7 @@ def Strip(v: str) ->str:
>>> s(' hello world ')
'hello world'
"""
- pass
+ return v.strip()
class DefaultTo(object):
diff --git a/voluptuous/validators.py b/voluptuous/validators.py
index 88b50f6..10230e1 100644
--- a/voluptuous/validators.py
+++ b/voluptuous/validators.py
@@ -41,7 +41,13 @@ def truth(f: typing.Callable) ->typing.Callable:
>>> with raises(MultipleInvalid, 'not a valid value'):
... validate('/notavaliddir')
"""
- pass
+ @wraps(f)
+ def wrapper(v):
+ t = f(v)
+ if not t:
+ raise ValueError
+ return v
+ return wrapper
class Coerce(object):
@@ -109,7 +115,7 @@ def IsTrue(v):
... except MultipleInvalid as e:
... assert isinstance(e.errors[0], TrueInvalid)
"""
- pass
+ return bool(v)
@message('value was not false', cls=FalseInvalid)
@@ -129,7 +135,7 @@ def IsFalse(v):
... except MultipleInvalid as e:
... assert isinstance(e.errors[0], FalseInvalid)
"""
- pass
+ return not bool(v)
@message('expected boolean', cls=BooleanInvalid)
@@ -153,7 +159,18 @@ def Boolean(v):
... except MultipleInvalid as e:
... assert isinstance(e.errors[0], BooleanInvalid)
"""
- pass
+ if isinstance(v, bool):
+ return v
+ if isinstance(v, str):
+ v = v.lower()
+ if v in ('1', 'true', 'yes', 'on', 'enable'):
+ return True
+ if v in ('0', 'false', 'no', 'off', 'disable'):
+ return False
+ try:
+ return bool(v)
+ except ValueError:
+ raise BooleanInvalid('expected boolean')
class _WithSubValidators(object):
@@ -894,7 +911,16 @@ class Number(object):
:param number:
:return: tuple(precision, scale, decimal_number)
"""
- pass
+ try:
+ decimal_num = Decimal(number)
+ except InvalidOperation:
+ raise Invalid(self.msg or f'{number} is not a valid Decimal')
+
+ sign, digits, exponent = decimal_num.as_tuple()
+ scale = -exponent if exponent < 0 else 0
+ precision = len(digits)
+
+ return precision, scale, decimal_num
class SomeOf(_WithSubValidators):