Skip to content

back to Claude Sonnet 3.5 - Fill-in summary

Claude Sonnet 3.5 - Fill-in: 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..135f37e 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,40 @@ 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, path: typing.Optional[typing.List[typing.Hashable]] = None,
+                 error_message: typing.Optional[str] = None,
+                 error_type: typing.Optional[str] = None,
+                 expected: int = 0, actual: int = 0) -> None:
+        super().__init__(msg, path, error_message, error_type)
+        self.expected = expected
+        self.actual = actual
+
+    def __str__(self) -> str:
+        return f"{super().__str__()} (expected at least {self.expected}, got {self.actual})"


 class TooManyValid(Invalid):
     """The value passed more than expected validations."""
-    pass
+    def __init__(self, msg: str, path: typing.Optional[typing.List[typing.Hashable]] = None,
+                 error_message: typing.Optional[str] = None,
+                 error_type: typing.Optional[str] = None,
+                 expected: int = 0, actual: int = 0) -> None:
+        super().__init__(msg, path, error_message, error_type)
+        self.expected = expected
+        self.actual = actual
+
+    def __str__(self) -> str:
+        return f"{super().__str__()} (expected at most {self.expected}, got {self.actual})"
diff --git a/voluptuous/humanize.py b/voluptuous/humanize.py
index 2902871..fabb7d0 100644
--- a/voluptuous/humanize.py
+++ b/voluptuous/humanize.py
@@ -11,4 +11,29 @@ 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 = ' -> '.join(str(p) for p in error.path) if error.path else 'root'
+        value = get_value_from_path(data, error.path)
+        value_str = str(value)[:max_sub_error_length]
+        if len(str(value)) > max_sub_error_length:
+            value_str += '...'
+        error_messages.append(f"Error at {path}: {error.error_message}. Got: {value_str}")
+
+    return '\n'.join(error_messages)
+
+def get_value_from_path(data, path):
+    """Helper function to get the value from the data using the error path."""
+    for key in path:
+        if isinstance(data, (list, tuple)) and isinstance(key, int):
+            data = data[key] if 0 <= key < len(data) else None
+        elif isinstance(data, dict):
+            data = data.get(key)
+        else:
+            return None
+    return data
diff --git a/voluptuous/schema_builder.py b/voluptuous/schema_builder.py
index de2b53c..df86a0a 100644
--- a/voluptuous/schema_builder.py
+++ b/voluptuous/schema_builder.py
@@ -113,7 +113,21 @@ 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, (int, float, str, bool)):
+                return type(value)
+            else:
+                return object
+
+        inferred_schema = infer_type(data)
+        return cls(inferred_schema, **kwargs)

     def __eq__(self, other):
         if not isinstance(other, Schema):
@@ -142,7 +156,34 @@ class Schema(object):

     def _compile_mapping(self, schema, invalid_msg=None):
         """Create validator for given mapping."""
-        pass
+        def validate_mapping(path, data):
+            if not isinstance(data, dict):
+                raise er.Invalid(invalid_msg or 'expected a dictionary', path)
+
+            out = type(data)()
+            for key, value in data.items():
+                key_path = path + [key]
+                if key in schema:
+                    out[key] = schema[key](key_path, value)
+                elif isinstance(key, str) and key.startswith('$'):
+                    out[key] = value
+                elif Any in schema:
+                    out[key] = schema[Any](key_path, value)
+                elif self.extra == ALLOW_EXTRA:
+                    out[key] = value
+                elif self.extra != REMOVE_EXTRA:
+                    raise er.Invalid('extra keys not allowed', key_path)
+
+            for key, validator in schema.items():
+                if isinstance(key, Required) and key.schema not in out:
+                    if key.default is not UNDEFINED:
+                        out[key.schema] = key.default()
+                    else:
+                        raise er.RequiredFieldInvalid('required key not provided', path + [key.schema])
+
+            return out
+
+        return validate_mapping

     def _compile_object(self, schema):
         """Validate an object.
@@ -162,7 +203,27 @@ class Schema(object):
             ...   validate(Structure(one='three'))

         """
-        pass
+        def validate_object(path, data):
+            if not isinstance(data, schema.cls):
+                raise er.Invalid(f'expected {schema.cls.__name__}', path)
+
+            out = schema.cls()
+            for key, validator in schema.items():
+                if hasattr(data, key):
+                    value = getattr(data, key)
+                    try:
+                        setattr(out, key, validator(path + [key], value))
+                    except er.Invalid as e:
+                        raise er.Invalid(f'not a valid value for object value', path + [key]) from e
+                elif isinstance(key, Required):
+                    if key.default is not UNDEFINED:
+                        setattr(out, key.schema, key.default())
+                    else:
+                        raise er.RequiredFieldInvalid('required attribute not provided', path + [key.schema])
+
+            return out
+
+        return validate_object

     def _compile_dict(self, schema):
         """Validate a dictionary.
@@ -240,7 +301,48 @@ class Schema(object):
          "expected str for dictionary value @ data['adict']['strfield']"]

         """
-        pass
+        def validate_dict(path, data):
+            if not isinstance(data, dict):
+                raise er.Invalid('expected a dictionary', path)
+
+            out = type(data)()
+            errors = []
+            for key, value in data.items():
+                key_path = path + [key]
+                if key in schema:
+                    try:
+                        out[key] = schema[key](key_path, value)
+                    except er.Invalid as e:
+                        errors.append(e)
+                elif isinstance(key, type):
+                    if any(isinstance(k, type) for k in schema):
+                        continue
+                    try:
+                        valid_key = next(k for k in schema if isinstance(k, type) and isinstance(key, k))
+                        out[key] = schema[valid_key](key_path, value)
+                    except StopIteration:
+                        if self.extra == ALLOW_EXTRA:
+                            out[key] = value
+                        elif self.extra != REMOVE_EXTRA:
+                            errors.append(er.Invalid('extra keys not allowed', key_path))
+                elif self.extra == ALLOW_EXTRA:
+                    out[key] = value
+                elif self.extra != REMOVE_EXTRA:
+                    errors.append(er.Invalid('extra keys not allowed', key_path))
+
+            for key in schema:
+                if isinstance(key, Required) and key not in out:
+                    if key.default is not UNDEFINED:
+                        out[key.schema] = key.default()
+                    else:
+                        errors.append(er.RequiredFieldInvalid('required key not provided', path + [key.schema]))
+
+            if errors:
+                raise er.MultipleInvalid(errors)
+
+            return out
+
+        return validate_dict

     def _compile_sequence(self, schema, seq_type):
         """Validate a sequence type.
@@ -255,7 +357,28 @@ class Schema(object):
         >>> validator([1])
         [1]
         """
-        pass
+        def validate_sequence(path, data):
+            if not isinstance(data, seq_type):
+                raise er.Invalid(f'expected a {seq_type.__name__}', path)
+
+            out = []
+            errors = []
+            for i, value in enumerate(data):
+                item_path = path + [i]
+                for validator in schema:
+                    try:
+                        out.append(validator(item_path, value))
+                        break
+                    except er.Invalid as e:
+                        if validator == schema[-1]:
+                            errors.append(e)
+
+            if errors:
+                raise er.MultipleInvalid(errors)
+
+            return seq_type(out)
+
+        return validate_sequence

     def _compile_tuple(self, schema):
         """Validate a tuple.
@@ -270,7 +393,7 @@ class Schema(object):
         >>> validator((1,))
         (1,)
         """
-        pass
+        return self._compile_sequence(schema, tuple)

     def _compile_list(self, schema):
         """Validate a list.
@@ -285,7 +408,7 @@ class Schema(object):
         >>> validator([1])
         [1]
         """
-        pass
+        return self._compile_sequence(schema, list)

     def _compile_set(self, schema):
         """Validate a set.
@@ -300,7 +423,28 @@ class Schema(object):
         >>> with raises(er.MultipleInvalid, 'invalid value in set'):
         ...   validator(set(['a']))
         """
-        pass
+        def validate_set(path, data):
+            if not isinstance(data, set):
+                raise er.Invalid('expected a set', path)
+
+            out = set()
+            errors = []
+            for i, value in enumerate(data):
+                item_path = path + [i]
+                for validator in schema:
+                    try:
+                        out.add(validator(item_path, value))
+                        break
+                    except er.Invalid as e:
+                        if validator == schema[-1]:
+                            errors.append(e)
+
+            if errors:
+                raise er.MultipleInvalid(errors)
+
+            return out
+
+        return validate_set

     def extend(self, schema: Schemable, required: typing.Optional[bool]=
         None, extra: typing.Optional[int]=None) ->Schema:
@@ -316,7 +460,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):
@@ -707,4 +861,16 @@ def validate(*a, **kw) ->typing.Callable:
         ...   return arg1 * 2

     """
-    pass
+    def decorator(func):
+        @wraps(func)
+        def wrapper(*args, **kwargs):
+            schema = Schema(dict(zip(func.__code__.co_varnames, a)), **kw)
+            bound_args = inspect.signature(func).bind(*args, **kwargs)
+            bound_args.apply_defaults()
+            validated_args = schema(dict(bound_args.arguments))
+            result = func(**validated_args)
+            if '__return__' in kw:
+                return Schema(kw['__return__'])(result)
+            return result
+        return wrapper
+    return 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..41ef694 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,9 @@ def IsFalse(v):
     ... except MultipleInvalid as e:
     ...   assert isinstance(e.errors[0], FalseInvalid)
     """
-    pass
+    if bool(v):
+        raise ValueError
+    return v


 @message('expected boolean', cls=BooleanInvalid)
@@ -153,7 +161,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):
@@ -213,6 +232,15 @@ class Any(_WithSubValidators):
     >>> with raises(MultipleInvalid, "Expected 1 2 or 3"):
     ...   validate(4)
     """
+    def _run(self, v):
+        errors = []
+        for validator in self._compiled:
+            try:
+                return validator(v)
+            except Invalid as e:
+                errors.append(e)
+        else:
+            raise AnyInvalid(self.msg or errors)


 Or = Any
@@ -255,6 +283,13 @@ class All(_WithSubValidators):
     >>> validate('10')
     10
     """
+    def _run(self, v):
+        try:
+            for validator in self._compiled:
+                v = validator(v)
+        except Invalid as e:
+            raise AllInvalid(self.msg or str(e))
+        return v


 And = All
@@ -340,7 +375,17 @@ def Email(v):
     >>> s('t@x.com')
     't@x.com'
     """
-    pass
+    try:
+        if not isinstance(v, str):
+            raise ValueError
+        user_part, domain_part = v.rsplit('@', 1)
+        if not USER_REGEX.match(user_part):
+            raise ValueError
+        if not DOMAIN_REGEX.match(domain_part):
+            raise ValueError
+    except ValueError:
+        raise EmailInvalid('expected an email address')
+    return v


 @message('expected a fully qualified domain name URL', cls=UrlInvalid)
@@ -353,7 +398,17 @@ def FqdnUrl(v):
     >>> s('http://w3.org')
     'http://w3.org'
     """
-    pass
+    try:
+        if not isinstance(v, str):
+            raise ValueError
+        parsed = urlparse.urlparse(v)
+        if not parsed.scheme or not parsed.netloc or parsed.netloc == 'localhost':
+            raise ValueError
+        if not DOMAIN_REGEX.match(parsed.netloc):
+            raise ValueError
+    except ValueError:
+        raise UrlInvalid('expected a fully qualified domain name URL')
+    return v


 @message('expected a URL', cls=UrlInvalid)
@@ -366,7 +421,15 @@ def Url(v):
     >>> s('http://w3.org')
     'http://w3.org'
     """
-    pass
+    try:
+        if not isinstance(v, str):
+            raise ValueError
+        parsed = urlparse.urlparse(v)
+        if not parsed.scheme or not parsed.netloc:
+            raise ValueError
+    except ValueError:
+        raise UrlInvalid('expected a URL')
+    return v


 @message('Not a file', cls=FileInvalid)
@@ -425,7 +488,11 @@ def Maybe(validator: Schemable, msg: typing.Optional[str]=None):
     ...  s("string")

     """
-    pass
+    def maybe_validator(v):
+        if v is None:
+            return v
+        return Schema(validator)(v)
+    return maybe_validator


 class Range(object):