### schema_builder 261-760

    def _compile_list(self, schema):
        """Validate a list.

        A list is a sequence of valid values or validators tried in order.

        >>> validator = Schema(['one', 'two', int])
        >>> validator(['one'])
        ['one']
        >>> with raises(er.MultipleInvalid, 'expected int @ data[0]'):
        ...   validator([3.5])
        >>> validator([1])
        [1]
        """
        pass

    def _compile_set(self, schema):
        """Validate a set.

        A set is an unordered collection of unique elements.

        >>> validator = Schema({int})
        >>> validator(set([42])) == set([42])
        True
        >>> with raises(er.Invalid, 'expected a set'):
        ...   validator(42)
        >>> with raises(er.MultipleInvalid, 'invalid value in set'):
        ...   validator(set(['a']))
        """
        pass

    def extend(self, schema: Schemable, required: typing.Optional[bool]=None, extra: typing.Optional[int]=None) -> Schema:
        """Create a new `Schema` by merging this and the provided `schema`.

        Neither this `Schema` nor the provided `schema` are modified. The
        resulting `Schema` inherits the `required` and `extra` parameters of
        this, unless overridden.

        Both schemas must be dictionary-based.

        :param schema: dictionary to extend this `Schema` with
        :param required: if set, overrides `required` of this `Schema`
        :param extra: if set, overrides `extra` of this `Schema`
        """
        pass

def _compile_scalar(schema):
    """A scalar value.

    The schema can either be a value or a type.

    >>> _compile_scalar(int)([], 1)
    1
    >>> with raises(er.Invalid, 'expected float'):
    ...   _compile_scalar(float)([], '1')

    Callables have
    >>> _compile_scalar(lambda v: float(v))([], '1')
    1.0

    As a convenience, ValueError's are trapped:

    >>> with raises(er.Invalid, 'not a valid value'):
    ...   _compile_scalar(lambda v: float(v))([], 'a')
    """
    pass

def _compile_itemsort():
    """return sort function of mappings"""
    pass
_sort_item = _compile_itemsort()

def _iterate_mapping_candidates(schema):
    """Iterate over schema in a meaningful order."""
    pass

def _iterate_object(obj):
    """Return iterator over object attributes. Respect objects with
    defined __slots__.

    """
    pass

class Msg(object):
    """Report a user-friendly message if a schema fails to validate.

    >>> validate = Schema(
    ...   Msg(['one', 'two', int],
    ...       'should be one of "one", "two" or an integer'))
    >>> with raises(er.MultipleInvalid, 'should be one of "one", "two" or an integer'):
    ...   validate(['three'])

    Messages are only applied to invalid direct descendants of the schema:

    >>> validate = Schema(Msg([['one', 'two', int]], 'not okay!'))
    >>> with raises(er.MultipleInvalid, 'expected int @ data[0][0]'):
    ...   validate([['three']])

    The type which is thrown can be overridden but needs to be a subclass of Invalid

    >>> with raises(er.SchemaError, 'Msg can only use subclases of Invalid as custom class'):
    ...   validate = Schema(Msg([int], 'should be int', cls=KeyError))

    If you do use a subclass of Invalid, that error will be thrown (wrapped in a MultipleInvalid)

    >>> validate = Schema(Msg([['one', 'two', int]], 'not okay!', cls=er.RangeInvalid))
    >>> try:
    ...  validate(['three'])
    ... except er.MultipleInvalid as e:
    ...   assert isinstance(e.errors[0], er.RangeInvalid)
    """

    def __init__(self, schema: Schemable, msg: str, cls: typing.Optional[typing.Type[Error]]=None) -> None:
        if cls and (not issubclass(cls, er.Invalid)):
            raise er.SchemaError('Msg can only use subclases of Invalid as custom class')
        self._schema = schema
        self.schema = Schema(schema)
        self.msg = msg
        self.cls = cls

    def __call__(self, v):
        try:
            return self.schema(v)
        except er.Invalid as e:
            if len(e.path) > 1:
                raise e
            else:
                raise (self.cls or er.Invalid)(self.msg)

    def __repr__(self):
        return 'Msg(%s, %s, cls=%s)' % (self._schema, self.msg, self.cls)

class Object(dict):
    """Indicate that we should work with attributes, not keys."""

    def __init__(self, schema: typing.Any, cls: object=UNDEFINED) -> None:
        self.cls = cls
        super(Object, self).__init__(schema)

class VirtualPathComponent(str):

    def __str__(self):
        return '<' + self + '>'

    def __repr__(self):
        return self.__str__()

class Marker(object):
    """Mark nodes for special treatment.

    `description` is an optional field, unused by Voluptuous itself, but can be
    introspected by any external tool, for example to generate schema documentation.
    """
    __slots__ = ('schema', '_schema', 'msg', 'description', '__hash__')

    def __init__(self, schema_: Schemable, msg: typing.Optional[str]=None, description: typing.Any | None=None) -> None:
        self.schema: typing.Any = schema_
        self._schema = Schema(schema_)
        self.msg = msg
        self.description = description
        self.__hash__ = cache(lambda: hash(schema_))

    def __call__(self, v):
        try:
            return self._schema(v)
        except er.Invalid as e:
            if not self.msg or len(e.path) > 1:
                raise
            raise er.Invalid(self.msg)

    def __str__(self):
        return str(self.schema)

    def __repr__(self):
        return repr(self.schema)

    def __lt__(self, other):
        if isinstance(other, Marker):
            return self.schema < other.schema
        return self.schema < other

    def __eq__(self, other):
        return self.schema == other

    def __ne__(self, other):
        return not self.schema == other

class Optional(Marker):
    """Mark a node in the schema as optional, and optionally provide a default

    >>> schema = Schema({Optional('key'): str})
    >>> schema({})
    {}
    >>> schema = Schema({Optional('key', default='value'): str})
    >>> schema({})
    {'key': 'value'}
    >>> schema = Schema({Optional('key', default=list): list})
    >>> schema({})
    {'key': []}

    If 'required' flag is set for an entire schema, optional keys aren't required

    >>> schema = Schema({
    ...    Optional('key'): str,
    ...    'key2': str
    ... }, required=True)
    >>> schema({'key2':'value'})
    {'key2': 'value'}
    """

    def __init__(self, schema: Schemable, msg: typing.Optional[str]=None, default: typing.Any=UNDEFINED, description: typing.Any | None=None) -> None:
        super(Optional, self).__init__(schema, msg=msg, description=description)
        self.default = default_factory(default)

class Exclusive(Optional):
    """Mark a node in the schema as exclusive.

    Exclusive keys inherited from Optional:

    >>> schema = Schema({Exclusive('alpha', 'angles'): int, Exclusive('beta', 'angles'): int})
    >>> schema({'alpha': 30})
    {'alpha': 30}

    Keys inside a same group of exclusion cannot be together, it only makes sense for dictionaries:

    >>> with raises(er.MultipleInvalid, "two or more values in the same group of exclusion 'angles' @ data[<angles>]"):
    ...   schema({'alpha': 30, 'beta': 45})

    For example, API can provides multiple types of authentication, but only one works in the same time:

    >>> msg = 'Please, use only one type of authentication at the same time.'
    >>> schema = Schema({
    ... Exclusive('classic', 'auth', msg=msg):{
    ...     Required('email'): str,
    ...     Required('password'): str
    ...     },
    ... Exclusive('internal', 'auth', msg=msg):{
    ...     Required('secret_key'): str
    ...     },
    ... Exclusive('social', 'auth', msg=msg):{
    ...     Required('social_network'): str,
    ...     Required('token'): str
    ...     }
    ... })

    >>> with raises(er.MultipleInvalid, "Please, use only one type of authentication at the same time. @ data[<auth>]"):
    ...     schema({'classic': {'email': 'foo@example.com', 'password': 'bar'},
    ...             'social': {'social_network': 'barfoo', 'token': 'tEMp'}})
    """

    def __init__(self, schema: Schemable, group_of_exclusion: str, msg: typing.Optional[str]=None, description: typing.Any | None=None) -> None:
        super(Exclusive, self).__init__(schema, msg=msg, description=description)
        self.group_of_exclusion = group_of_exclusion

class Inclusive(Optional):
    """Mark a node in the schema as inclusive.

    Inclusive keys inherited from Optional:

    >>> schema = Schema({
    ...     Inclusive('filename', 'file'): str,
    ...     Inclusive('mimetype', 'file'): str
    ... })
    >>> data = {'filename': 'dog.jpg', 'mimetype': 'image/jpeg'}
    >>> data == schema(data)
    True

    Keys inside a same group of inclusive must exist together, it only makes sense for dictionaries:

    >>> with raises(er.MultipleInvalid, "some but not all values in the same group of inclusion 'file' @ data[<file>]"):
    ...     schema({'filename': 'dog.jpg'})

    If none of the keys in the group are present, it is accepted:

    >>> schema({})
    {}

    For example, API can return 'height' and 'width' together, but not separately.

    >>> msg = "Height and width must exist together"
    >>> schema = Schema({
    ...     Inclusive('height', 'size', msg=msg): int,
    ...     Inclusive('width', 'size', msg=msg): int
    ... })

    >>> with raises(er.MultipleInvalid, msg + " @ data[<size>]"):
    ...     schema({'height': 100})

    >>> with raises(er.MultipleInvalid, msg + " @ data[<size>]"):
    ...     schema({'width': 100})

    >>> data = {'height': 100, 'width': 100}
    >>> data == schema(data)
    True
    """

    def __init__(self, schema: Schemable, group_of_inclusion: str, msg: typing.Optional[str]=None, description: typing.Any | None=None, default: typing.Any=UNDEFINED) -> None:
        super(Inclusive, self).__init__(schema, msg=msg, default=default, description=description)
        self.group_of_inclusion = group_of_inclusion

class Required(Marker):
    """Mark a node in the schema as being required, and optionally provide a default value.

    >>> schema = Schema({Required('key'): str})
    >>> with raises(er.MultipleInvalid, "required key not provided @ data['key']"):
    ...   schema({})

    >>> schema = Schema({Required('key', default='value'): str})
    >>> schema({})
    {'key': 'value'}
    >>> schema = Schema({Required('key', default=list): list})
    >>> schema({})
    {'key': []}
    """

    def __init__(self, schema: Schemable, msg: typing.Optional[str]=None, default: typing.Any=UNDEFINED, description: typing.Any | None=None) -> None:
        super(Required, self).__init__(schema, msg=msg, description=description)
        self.default = default_factory(default)

class Remove(Marker):
    """Mark a node in the schema to be removed and excluded from the validated
    output. Keys that fail validation will not raise ``Invalid``. Instead, these
    keys will be treated as extras.

    >>> schema = Schema({str: int, Remove(int): str})
    >>> with raises(er.MultipleInvalid, "extra keys not allowed @ data[1]"):
    ...    schema({'keep': 1, 1: 1.0})
    >>> schema({1: 'red', 'red': 1, 2: 'green'})
    {'red': 1}
    >>> schema = Schema([int, Remove(float), Extra])
    >>> schema([1, 2, 3, 4.0, 5, 6.0, '7'])
    [1, 2, 3, 5, '7']
    """

    def __init__(self, schema_: Schemable, msg: typing.Optional[str]=None, description: typing.Any | None=None) -> None:
        super().__init__(schema_, msg, description)
        self.__hash__ = cache(lambda: object.__hash__(self))

    def __call__(self, schema: Schemable):
        super(Remove, self).__call__(schema)
        return self.__class__

    def __repr__(self):
        return 'Remove(%r)' % (self.schema,)

def message(default: typing.Optional[str]=None, cls: typing.Optional[typing.Type[Error]]=None) -> typing.Callable:
    """Convenience decorator to allow functions to provide a message.

    Set a default message:

        >>> @message('not an integer')
        ... def isint(v):
        ...   return int(v)

        >>> validate = Schema(isint())
        >>> with raises(er.MultipleInvalid, 'not an integer'):
        ...   validate('a')

    The message can be overridden on a per validator basis:

        >>> validate = Schema(isint('bad'))
        >>> with raises(er.MultipleInvalid, 'bad'):
        ...   validate('a')

    The class thrown too:

        >>> class IntegerInvalid(er.Invalid): pass
        >>> validate = Schema(isint('bad', clsoverride=IntegerInvalid))
        >>> try:
        ...  validate('a')
        ... except er.MultipleInvalid as e:
        ...   assert isinstance(e.errors[0], IntegerInvalid)
    """
    pass

def _args_to_dict(func, args):
    """Returns argument names as values as key-value pairs."""
    pass

def _merge_args_with_kwargs(args_dict, kwargs_dict):
    """Merge args with kwargs."""
    pass

def validate(*a, **kw) -> typing.Callable:
    """Decorator for validating arguments of a function against a given schema.

    Set restrictions for arguments:

        >>> @validate(arg1=int, arg2=int)
        ... def foo(arg1, arg2):
        ...   return arg1 * arg2

    Set restriction for returned value:

        >>> @validate(arg=int, __return__=int)
        ... def bar(arg1):
        ...   return arg1 * 2

    """
    pass### validators 121-920
    """
    pass

@message('expected boolean', cls=BooleanInvalid)
def Boolean(v):
    """Convert human-readable boolean values to a bool.

    Accepted values are 1, true, yes, on, enable, and their negatives.
    Non-string values are cast to bool.

    >>> validate = Schema(Boolean())
    >>> validate(True)
    True
    >>> validate("1")
    True
    >>> validate("0")
    False
    >>> with raises(MultipleInvalid, "expected boolean"):
    ...   validate('moo')
    >>> try:
    ...  validate('moo')
    ... except MultipleInvalid as e:
    ...   assert isinstance(e.errors[0], BooleanInvalid)
    """
    pass

class _WithSubValidators(object):
    """Base class for validators that use sub-validators.

    Special class to use as a parent class for validators using sub-validators.
    This class provides the `__voluptuous_compile__` method so the
    sub-validators are compiled by the parent `Schema`.
    """

    def __init__(self, *validators, msg=None, required=False, discriminant=None, **kwargs) -> None:
        self.validators = validators
        self.msg = msg
        self.required = required
        self.discriminant = discriminant

    def __voluptuous_compile__(self, schema: Schema) -> typing.Callable:
        self._compiled = []
        old_required = schema.required
        self.schema = schema
        for v in self.validators:
            schema.required = self.required
            self._compiled.append(schema._compile(v))
    <response clipped><NOTE>Due to the max output limit, only part of the full response has been shown to you.</NOTE>ex="^contains unhashable elements: "):
    ...   s([set([1, 2]), set([3, 4])])
    >>> s('abc')
    'abc'
    >>> with raises(Invalid, regex="^contains duplicate items: "):
    ...   s('aabbc')
    """

    def __init__(self, msg: typing.Optional[str]=None) -> None:
        self.msg = msg

    def __call__(self, v):
        try:
            set_v = set(v)
        except TypeError as e:
            raise TypeInvalid(self.msg or 'contains unhashable elements: {0}'.format(e))
        if len(set_v) != len(v):
            seen = set()
            dupes = list(set((x for x in v if x in seen or seen.add(x))))
            raise Invalid(self.msg or 'contains duplicate items: {0}'.format(dupes))
        return v

    def __repr__(self):
        return 'Unique()'

class Equal(object):
    """Ensure that value matches target.

    >>> s = Schema(Equal(1))
    >>> s(1)
    1
    >>> with raises(Invalid):
    ...    s(2)

    Validators are not supported, match must be exact:

    >>> s = Schema(Equal(str))
    >>> with raises(Invalid):
    ...     s('foo')
    """

    def __init__(self, target, msg: typing.Optional[str]=None) -> None:
        self.target = target
        self.msg = msg

    def __call__(self, v):
        if v != self.target:
            raise Invalid(self.msg or 'Values are not equal: value:{} != target:{}'.format(v, self.target))
        return v

    def __repr__(self):
        return 'Equal({})'.format(self.target)

class Unordered(object):
    """Ensures sequence contains values in unspecified order.

    >>> s = Schema(Unordered([2, 1]))
    >>> s([2, 1])
    [2, 1]
    >>> s([1, 2])
    [1, 2]
    >>> s = Schema(Unordered([str, int]))
    >>> s(['foo', 1])
    ['foo', 1]
    >>> s([1, 'foo'])
    [1, 'foo']
    """

    def __init__(self, validators: typing.Iterable[Schemable], msg: typing.Optional[str]=None, **kwargs) -> None:
        self.validators = validators
        self.msg = msg
        self._schemas = [Schema(val, **kwargs) for val in validators]

    def __call__(self, v):
        if not isinstance(v, (list, tuple)):
            raise Invalid(self.msg or 'Value {} is not sequence!'.format(v))
        if len(v) != len(self._schemas):
            raise Invalid(self.msg or 'List lengths differ, value:{} != target:{}'.format(len(v), len(self._schemas)))
        consumed = set()
        missing = []
        for index, value in enumerate(v):
            found = False
            for i, s in enumerate(self._schemas):
                if i in consumed:
                    continue
                try:
                    s(value)
                except Invalid:
                    pass
                else:
                    found = True
                    consumed.add(i)
                    break
            if not found:
                missing.append((index, value))
        if len(missing) == 1:
            el = missing[0]
            raise Invalid(self.msg or 'Element #{} ({}) is not valid against any validator'.format(el[0], el[1]))
        elif missing:
            raise MultipleInvalid([Invalid(self.msg or 'Element #{} ({}) is not valid against any validator'.format(el[0], el[1])) for el in missing])
        return v

    def __repr__(self):
        return 'Unordered([{}])'.format(', '.join((repr(v) for v in self.validators)))

class Number(object):
    """
    Verify the number of digits that are present in the number(Precision),
    and the decimal places(Scale).

    :raises Invalid: If the value does not match the provided Precision and Scale.

    >>> schema = Schema(Number(precision=6, scale=2))
    >>> schema('1234.01')
    '1234.01'
    >>> schema = Schema(Number(precision=6, scale=2, yield_decimal=True))
    >>> schema('1234.01')
    Decimal('1234.01')
    """

    def __init__(self, precision: typing.Optional[int]=None, scale: typing.Optional[int]=None, msg: typing.Optional[str]=None, yield_decimal: bool=False) -> None:
        self.precision = precision
        self.scale = scale
        self.msg = msg
        self.yield_decimal = yield_decimal

    def __call__(self, v):
        """
        :param v: is a number enclosed with string
        :return: Decimal number
        """
        precision, scale, decimal_num = self._get_precision_scale(v)
        if self.precision is not None and self.scale is not None and (precision != self.precision) and (scale != self.scale):
            raise Invalid(self.msg or 'Precision must be equal to %s, and Scale must be equal to %s' % (self.precision, self.scale))
        else:
            if self.precision is not None and precision != self.precision:
                raise Invalid(self.msg or 'Precision must be equal to %s' % self.precision)
            if self.scale is not None and scale != self.scale:
                raise Invalid(self.msg or 'Scale must be equal to %s' % self.scale)
        if self.yield_decimal:
            return decimal_num
        else:
            return v

    def __repr__(self):
        return 'Number(precision=%s, scale=%s, msg=%s)' % (self.precision, self.scale, self.msg)

    def _get_precision_scale(self, number) -> typing.Tuple[int, int, Decimal]:
        """
        :param number:
        :return: tuple(precision, scale, decimal_number)
        """
        pass

class SomeOf(_WithSubValidators):
    """Value must pass at least some validations, determined by the given parameter.
    Optionally, number of passed validations can be capped.

    The output of each validator is passed as input to the next.

    :param min_valid: Minimum number of valid schemas.
    :param validators: List of schemas or validators to match input against.
    :param max_valid: Maximum number of valid schemas.
    :param msg: Message to deliver to user if validation fails.
    :param kwargs: All other keyword arguments are passed to the sub-schema constructors.

    :raises NotEnoughValid: If the minimum number of validations isn't met.
    :raises TooManyValid: If the maximum number of validations is exceeded.

    >>> validate = Schema(SomeOf(min_valid=2, validators=[Range(1, 5), Any(float, int), 6.6]))
    >>> validate(6.6)
    6.6
    >>> validate(3)
    3
    >>> with raises(MultipleInvalid, 'value must be at most 5, not a valid value'):
    ...     validate(6.2)
    """

    def __init__(self, validators: typing.List[Schemable], min_valid: typing.Optional[int]=None, max_valid: typing.Optional[int]=None, **kwargs) -> None:
        assert min_valid is not None or max_valid is not None, 'when using "%s" you should specify at least one of min_valid and max_valid' % (type(self).__name__,)
        self.min_valid = min_valid or 0
        self.max_valid = max_valid or len(validators)
        super(SomeOf, self).__init__(*validators, **kwargs)

    def __repr__(self):
        return 'SomeOf(min_valid=%s, validators=[%s], max_valid=%s, msg=%r)' % (self.min_valid, ', '.join((repr(v) for v in self.validators)), self.max_valid, self.msg)### tests selected
# fmt: off
import collections
import copy
import os
import sys
from enum import Enum

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,
)
from voluptuous.humanize import humanize_error
from voluptuous.util import Capitalize, Lower, Strip, Title, Upper

# fmt: on


def test_new_required_test():
    schema = Schema(
        {
            'my_key': All(int, Range(1, 20)),
        },
        required=True,
    )
    assert schema.required


def test_exact_sequence():
    schema = Schema(ExactSequence([int, int]))
    with raises(Invalid):
        schema([1, 2, 3])
    assert schema([1, 2]) == [1, 2]


def test_required():
    """Verify that Required works."""
    schema = Schema({Required('q'): int})
    schema({"q": 123})
    with raises(Invalid, "required key not provided @ data['q']"):
        schema({})


def test_extra_with_required():
    """Verify that Required does not break Extra."""
    schema = Schema({Required('toaster'): str, Extra: object})
    r = schema({'toaster': 'blue', 'another_valid_key': 'another_valid_value'})
    assert r == {'toaster': 'blue', 'another_valid_key': 'another_valid_value'}


def test_iterate_candidates():
    """Verify that the order for iterating over mapping candidates is right."""
    schema = {
        "toaster": str,
        Extra: object,
    }
    # toaster should be first.
    from voluptuous.schema_builder import _iterate_mapping_candidates

    assert _iterate_mapping_candidates(schema)[0][0] == 'toaster'


def test_in():
    """Verify that In works."""
    schema = Schema({"color": In(frozenset(["red", "blue", "yellow"]))})
    schema({"color": "blue"})
    with pytest.raises(
        MultipleInvalid,
        match=r"value must be one of \['blue', 'red', 'yellow'\] for dictionary value @ data\['color'\]",
    ) as ctx:
        schema({"color": "orange"})
    assert len(ctx.value.errors) == 1
    assert isinstance(ctx.value.errors[0], InInvalid)


def test_in_unsortable_container():
    """Verify that In works with unsortable container."""
    schema = Schema({"type": In((int, str, float))})
    schema({"type": float})
    with pytest.raises(
        MultipleInvalid,
        match=(
            r"value must be one of \[<class 'float'>, <class 'int'>, <class 'str'>\] for dictionary value "
            r"@ data\['type'\]"
        ),
    ) as ctx:
        schema({"type": 42})
    assert len(ctx.value.errors) == 1
    assert isinstance(ctx.value.errors[0], InInvalid)


def test_not_in():
    """Verify that NotIn works."""
    schema = Schema({"color": NotIn(frozenset(["red", "blue", "yellow"]))})
    schema({"color": "orange"})
    with pytest.raises(
        MultipleInvalid,
        match=(
            r"value must not be one of \['blue', 'red', 'yellow'\] for dictionary "
            r"value @ data\['color'\]"
        ),
    ) as ctx:
        schema({"color": "blue"})
    assert len(ctx.value.errors) == 1
    assert isinstance(ctx.value.errors[0], NotInInvalid)


def test_not_in_unsortable_container():
    """Verify that NotIn works with unsortable container."""
    schema = Schema({"type": NotIn((int, str, float))})
    schema({"type": 42})
    with pytest.raises(
        MultipleInvalid,
        match=(
            r"value must not be one of \[<class 'float'>, <class 'int'>, "
            r"<class 'str'>\] for dictionary value @ data\['type'\]"
        ),
    ) as ctx:
        schema({"type": str})
    assert len(ctx.value.errors) == 1
    assert isinstance(ctx.value.errors[0], NotInInvalid)


def test_contains():
    """Verify contains validation method."""
    schema = Schema({'color': Contains('red')})
    schema({'color': ['blue', 'red', 'yellow']})
    with pytest.raises(
        MultipleInvalid,
        match=r"value is not allowed for dictionary value @ data\['color'\]",
    ) as ctx:
        schema({'color': ['blue', 'yellow']})
    assert len(ctx.value.errors) == 1
    assert isinstance(ctx.value.errors[0], ContainsInvalid)


def test_remove():
    """Verify that Remove works."""
    # remove dict keys
    schema = Schema({"weight": int, Remove("color"): str, Remove("amount"): int})
    out_ = schema({"weight": 10, "color": "red", "amount": 1})
    assert "color" not in out_ and "amount" not in out_

    # remove keys by type
    schema = Schema(
        {
            "weight": float,
            "amount": int,
            # remove str keys with int values
            Remove(str): int,
            # keep str keys with str values
            str: str,
        }
    )
    out_ = schema({"weight": 73.4, "condition": "new", "amount": 5, "left": 2})
    # amount should stay since it's defined
    # other string keys with int values will be removed
    assert "amount" in out_ and "left" not in out_
    # string keys with string values will stay
    assert "condition" in out_

    # remove value from list
    schema = Schema([Remove(1), int])
    out_ = schema([1, 2, 3, 4, 1, 5, 6, 1, 1, 1])
    assert out_ == [2, 3, 4, 5, 6]

    # remove values from list by type
    schema = Schema([1.0, Remove(float), int])
    out_ = schema([1, 2, 1.0, 2.0, 3.0, 4])
    assert out_ == [1, 2, 1.0, 4]


def test_remove_with_error():
    def starts_with_dot(key: str) -> str:
        """Check if key starts with dot."""
        if not key.startswith("."):
            raise Invalid("Key does not start with .")
        return key

    def does_not_start_with_dot(key: str) -> str:
        """Check if key does not start with dot."""
        if key.startswith("."):
            raise Invalid("Key starts with .")
        return key

    schema = Schema(
        {
            Remove(All(str, starts_with_dot)): object,
            does_not_start_with_dot: Any(None),
        }
    )
    out_ = schema({".remove": None, "ok": None})
    assert ".remove" not in out_ and "ok" in out_


def test_extra_empty_errors():
    schema = Schema({'a': {Extra: object}}, required=True)
    schema({'a': {}})


def test_literal():
    """Test with Literal"""

    schema = Schema([Literal({"a": 1}), Literal({"b": 1})])
    schema([{"a": 1}])
    schema([{"b": 1}])
    schema([{"a": 1}, {"b": 1}])

    with pytest.raises(
        MultipleInvalid, match=r"\{'c': 1\} not match for \{'b': 1\} @ data\[0\]"
    ) as ctx:
        schema([{"c": 1}])
    assert len(ctx.value.errors) == 1
    assert isinstance(ctx.value.errors[0], LiteralInvalid)

    schema = Schema(Literal({"a": 1}))
    with pytest.raises(
        MultipleInvalid, match=r"\{'b': 1\} not match for \{'a': 1\}"
    ) as ctx:
        schema({"b": 1})
    assert len(ctx.value.errors) == 1
    assert isinstance(ctx.value.errors[0], LiteralInvalid)


def test_class():
    class C1:
        pass

    schema = Schema(C1)
    schema(C1())

    with pytest.raises(MultipleInvalid, match=r"expected C1") as ctx:
        schema(None)
    assert len(ctx.value.errors) == 1
    assert isinstance(ctx.value.errors[0], TypeInvalid)


def test_email_validation():
    """Test with valid email address"""
    schema = Schema({"email": Email()})
    out_ = schema({"email": "example@example.com"})

    assert 'example@example.com"', out_.get("url")


def test_email_validation_with_none():
    """Test with invalid None email address"""
    schema = Schema({"email": Email()})
    with pytest.raises(
        MultipleInvalid,
        match=r"expected an email address for dictionary value @ data\['email'\]",
    ) as ctx:
        schema({"email": None})
    assert len(ctx.value.errors) == 1
[The command completed with exit code 0.]
[Current working directory: /workspace/voluptuous]
[Python interpreter: /usr/bin/python]
[Command finished with exit code 0]