def from_typing_type(thing): # We start with special-case support for Union and Tuple - the latter # isn't actually a generic type. Support for Callable may be added to # this section later. # We then explicitly error on non-Generic types, which don't carry enough # information to sensibly resolve to strategies at runtime. # Finally, we run a variation of the subclass lookup in st.from_type # among generic types in the lookup. import typing # Under 3.6 Union is handled directly in st.from_type, as the argument is # not an instance of `type`. However, under Python 3.5 Union *is* a type # and we have to handle it here, including failing if it has no parameters. if hasattr(thing, '__union_params__'): # pragma: no cover args = sorted(thing.__union_params__ or (), key=type_sorting_key) if not args: raise ResolutionFailed('Cannot resolve Union of no types.') return st.one_of([st.from_type(t) for t in args]) if isinstance(thing, typing.TupleMeta): elem_types = getattr(thing, '__tuple_params__', None) or () elem_types += getattr(thing, '__args__', None) or () if getattr(thing, '__tuple_use_ellipsis__', False) or \ len(elem_types) == 2 and elem_types[-1] is Ellipsis: return st.lists(st.from_type(elem_types[0])).map(tuple) return st.tuples(*map(st.from_type, elem_types)) # Now, confirm that we're dealing with a generic type as we expected if not isinstance(thing, typing.GenericMeta): # pragma: no cover raise ResolutionFailed('Cannot resolve %s to a strategy' % (thing, )) # Parametrised generic types have their __origin__ attribute set to the # un-parametrised version, which we need to use in the subclass checks. # e.g.: typing.List[int].__origin__ == typing.List mapping = { k: v for k, v in _global_type_lookup.items() if isinstance(k, typing.GenericMeta) and try_issubclass(k, getattr(thing, '__origin__', None) or thing) } if typing.Dict in mapping: # The subtype relationships between generic and concrete View types # are sometimes inconsistent under Python 3.5, so we pop them out to # preserve our invariant that all examples of from_type(T) are # instances of type T - and simplify the strategy for abstract types # such as Container for t in (typing.KeysView, typing.ValuesView, typing.ItemsView): mapping.pop(t, None) strategies = [ v if isinstance(v, st.SearchStrategy) else v(thing) for k, v in mapping.items() if sum( try_issubclass(k, T) for T in mapping) == 1 ] empty = ', '.join(repr(s) for s in strategies if s.is_empty) if empty or not strategies: # pragma: no cover raise ResolutionFailed( 'Could not resolve %s to a strategy; consider using ' 'register_type_strategy' % (empty or thing, )) return st.one_of(strategies)
def from_field(field: F) -> st.SearchStrategy[Union[F, None]]: """Return a strategy for values that fit the given field. This function is used by :func:`~hypothesis.extra.django.from_form` and :func:`~hypothesis.extra.django.from_model` for any fields that require a value, or for which you passed ``...`` (:obj:`python:Ellipsis`) to infer a strategy from an annotation. It's pretty similar to the core :func:`~hypothesis.strategies.from_type` function, with a subtle but important difference: ``from_field`` takes a Field *instance*, rather than a Field *subtype*, so that it has access to instance attributes such as string length and validators. """ check_type((dm.Field, df.Field), field, "field") if getattr(field, "choices", False): choices: list = [] for value, name_or_optgroup in field.choices: if isinstance(name_or_optgroup, (list, tuple)): choices.extend(key for key, _ in name_or_optgroup) else: choices.append(value) # form fields automatically include an empty choice, strip it out if "" in choices: choices.remove("") min_size = 1 if isinstance(field, (dm.CharField, dm.TextField)) and field.blank: choices.insert(0, "") elif isinstance(field, (df.Field)) and not field.required: choices.insert(0, "") min_size = 0 strategy = st.sampled_from(choices) if isinstance(field, (df.MultipleChoiceField, df.TypedMultipleChoiceField)): strategy = st.lists(st.sampled_from(choices), min_size=min_size) else: if type(field) not in _global_field_lookup: if getattr(field, "null", False): return st.none() raise ResolutionFailed(f"Could not infer a strategy for {field!r}") strategy = _global_field_lookup[type(field)] # type: ignore if not isinstance(strategy, st.SearchStrategy): strategy = strategy(field) assert isinstance(strategy, st.SearchStrategy) if field.validators: def validate(value): try: field.run_validators(value) return True except django.core.exceptions.ValidationError: return False strategy = strategy.filter(validate) if getattr(field, "null", False): return st.none() | strategy return strategy
def from_attrs_attribute(attrib, target): """Infer a strategy from the metadata on an attr.Attribute object.""" # Try inferring from the default argument. Note that this will only help if # the user passed `infer` to builds() for this attribute, but in that case # we use it as the minimal example. default = st.nothing() if isinstance(attrib.default, attr.Factory): if not attrib.default.takes_self: default = st.builds(attrib.default.factory) elif attrib.default is not attr.NOTHING: default = st.just(attrib.default) # Try inferring None, exact values, or type from attrs provided validators. null = st.nothing() # updated to none() on seeing an OptionalValidator in_collections = [] # list of in_ validator collections to sample from validator_types = set() # type constraints to pass to types_to_strategy() if attrib.validator is not None: validator = attrib.validator if isinstance(validator, attr.validators._OptionalValidator): null = st.none() validator = validator.validator if isinstance(validator, attr.validators._AndValidator): vs = validator._validators else: vs = [validator] for v in vs: if isinstance(v, attr.validators._InValidator): if isinstance(v.options, string_types): in_collections.append(list(all_substrings(v.options))) else: in_collections.append(v.options) elif isinstance(v, attr.validators._InstanceOfValidator): validator_types.add(v.type) # This is the important line. We compose the final strategy from various # parts. The default value, if any, is the minimal shrink, followed by # None (again, if allowed). We then prefer to sample from values passed # to an in_ validator if available, but infer from a type otherwise. # Pick one because (sampled_from((1, 2)) | from_type(int)) would usually # fail validation by generating e.g. zero! if in_collections: sample = st.sampled_from(list(ordered_intersection(in_collections))) strat = default | null | sample else: strat = default | null | types_to_strategy(attrib, validator_types) # Better to give a meaningful error here than an opaque "could not draw" # when we try to get a value but have lost track of where this was created. if strat.is_empty: raise ResolutionFailed( "Cannot infer a strategy from the default, validator, type, or " "converter for attribute=%r of class=%r" % (attrib, target) ) return strat
def _for_form_ip(field): # the IP address form fields have no direct indication of which type # of address they want, so direct comparison with the validator # function has to be used instead. Sorry for the potato logic here if validate_ipv46_address in field.default_validators: return st.ip_addresses(v=4).map(str) | _ipv6_strings if validate_ipv4_address in field.default_validators: return st.ip_addresses(v=4).map(str) if validate_ipv6_address in field.default_validators: return _ipv6_strings raise ResolutionFailed(f"No IP version validator on field={field!r}")
def resolve_Type(thing): if getattr(thing, "__args__", None) is None: # This branch is for Python < 3.8, when __args__ was not always tracked return st.just(type) # pragma: no cover args = (thing.__args__[0], ) if getattr(args[0], "__origin__", None) is typing.Union: args = args[0].__args__ # Duplicate check from from_type here - only paying when needed. for a in args: # pragma: no cover # only on Python 3.6 if type(a) == ForwardRef: raise ResolutionFailed( "thing=%s cannot be resolved. Upgrading to " "python>=3.6 may fix this problem via improvements " "to the typing module." % (thing, )) return st.sampled_from(sorted(args, key=type_sorting_key))
def resolve_Type(thing): if thing.__args__ is None: return st.just(type) args = (thing.__args__[0], ) if getattr(args[0], "__origin__", None) is typing.Union: args = args[0].__args__ elif hasattr(args[0], "__union_params__"): # pragma: no cover args = args[0].__union_params__ if isinstance(ForwardRef, type): # pragma: no cover # Duplicate check from from_type here - only paying when needed. for a in args: if type(a) == ForwardRef: raise ResolutionFailed( "thing=%s cannot be resolved. Upgrading to " "python>=3.6 may fix this problem via improvements " "to the typing module." % (thing, )) return st.sampled_from(sorted(args, key=type_sorting_key))
def resolve_Type(thing): if getattr(thing, "__args__", None) is None: # This branch is for Python < 3.8, when __args__ was not always tracked return st.just(type) # pragma: no cover args = (thing.__args__[0], ) if is_a_union(args[0]): args = args[0].__args__ # Duplicate check from from_type here - only paying when needed. args = list(args) for i, a in enumerate(args): if type(a) == typing.ForwardRef: try: args[i] = getattr(builtins, a.__forward_arg__) except AttributeError: raise ResolutionFailed( f"Cannot find the type referenced by {thing} - try using " f"st.register_type_strategy({thing}, st.from_type(...))" ) from None return st.sampled_from(sorted(args, key=type_sorting_key))
def _try_import_forward_ref(thing, bound): # pragma: no cover """ Tries to import a real bound type from ``TypeVar`` bound to a ``ForwardRef``. This function is very "magical" to say the least, please don't use it. This function fully covered, but is excluded from coverage because we can only cover each path in a separate python version. """ try: return typing._eval_type(bound, vars(sys.modules[thing.__module__]), None) except (KeyError, AttributeError, NameError): if (isinstance(thing, typing.TypeVar) and getattr(thing, "__module__", None) == "typing"): raise ResolutionFailed( "It looks like you're using a TypeVar bound to a ForwardRef on Python " "3.6, which is not supported - try ugrading to Python 3.7 or later." ) from None # We fallback to `ForwardRef` instance, you can register it as a type as well: # >>> from typing import ForwardRef # >>> from hypothesis import strategies as st # >>> st.register_type_strategy(ForwardRef('YourType'), your_strategy) return bound
def from_typing_type(thing): # We start with special-case support for Union and Tuple - the latter # isn't actually a generic type. Then we handle Literal since it doesn't # support `isinstance`. # # We then explicitly error on non-Generic types, which don't carry enough # information to sensibly resolve to strategies at runtime. # Finally, we run a variation of the subclass lookup in `st.from_type` # among generic types in the lookup. if getattr(thing, "__origin__", None) == tuple or isinstance( thing, getattr(typing, "TupleMeta", ())): elem_types = getattr(thing, "__tuple_params__", None) or () elem_types += getattr(thing, "__args__", None) or () if (getattr(thing, "__tuple_use_ellipsis__", False) or len(elem_types) == 2 and elem_types[-1] is Ellipsis): return st.lists(st.from_type(elem_types[0])).map(tuple) elif len(elem_types) == 1 and elem_types[0] == (): return st.tuples() # Empty tuple; see issue #1583 return st.tuples(*map(st.from_type, elem_types)) if hasattr(typing, "Final") and getattr(thing, "__origin__", None) == typing.Final: return st.one_of([st.from_type(t) for t in thing.__args__]) if is_typing_literal(thing): args_dfs_stack = list(thing.__args__) literals = [] while args_dfs_stack: arg = args_dfs_stack.pop() if is_typing_literal(arg): args_dfs_stack.extend(reversed(arg.__args__)) else: literals.append(arg) return st.sampled_from(literals) # Now, confirm that we're dealing with a generic type as we expected if sys.version_info[:2] < (3, 9) and not isinstance( thing, typing_root_type): # pragma: no cover raise ResolutionFailed(f"Cannot resolve {thing} to a strategy") # Some "generic" classes are not generic *in* anything - for example both # Hashable and Sized have `__args__ == ()` on Python 3.7 or later. # (In 3.6 they're just aliases for the collections.abc classes) origin = getattr(thing, "__origin__", thing) if (typing.Hashable is not collections.abc.Hashable and origin in vars(collections.abc).values() and len(getattr(thing, "__args__", None) or []) == 0): return st.from_type(origin) # Parametrised generic types have their __origin__ attribute set to the # un-parametrised version, which we need to use in the subclass checks. # e.g.: typing.List[int].__origin__ == typing.List mapping = { k: v for k, v in _global_type_lookup.items() if is_generic_type(k) and try_issubclass(k, thing) } if typing.Dict in mapping or typing.Set in mapping: # ItemsView can cause test_lookup.py::test_specialised_collection_types # to fail, due to weird isinstance behaviour around the elements. mapping.pop(typing.ItemsView, None) if sys.version_info[:2] == (3, 6): # pragma: no cover # `isinstance(dict().values(), Container) is False` on py36 only -_- mapping.pop(typing.ValuesView, None) if typing.Deque in mapping and len(mapping) > 1: # Resolving generic sequences to include a deque is more trouble for e.g. # the ghostwriter than it's worth, via undefined names in the repr. mapping.pop(typing.Deque) if len(mapping) > 1: # issubclass treats bytestring as a kind of sequence, which it is, # but treating it as such breaks everything else when it is presumed # to be a generic sequence or container that could hold any item. # Except for sequences of integers, or unions which include integer! # See https://github.com/HypothesisWorks/hypothesis/issues/2257 # # This block drops ByteString from the types that can be generated # if there is more than one allowed type, and the element type is # not either `int` or a Union with `int` as one of its elements. elem_type = (getattr(thing, "__args__", None) or ["not int"])[0] if getattr(elem_type, "__origin__", None) is typing.Union: union_elems = elem_type.__args__ else: union_elems = () if not any( isinstance(T, type) and issubclass(int, T) for T in list(union_elems) + [elem_type]): mapping.pop(typing.ByteString, None) strategies = [ v if isinstance(v, st.SearchStrategy) else v(thing) for k, v in mapping.items() if sum( try_issubclass(k, T) for T in mapping) == 1 ] empty = ", ".join(repr(s) for s in strategies if s.is_empty) if empty or not strategies: raise ResolutionFailed( "Could not resolve %s to a strategy; consider using " "register_type_strategy" % (empty or thing, )) return st.one_of(strategies)
def from_typing_type(thing): # We start with special-case support for Union and Tuple - the latter # isn't actually a generic type. Then we handle Literal since it doesn't # support `isinstance`. # # We then explicitly error on non-Generic types, which don't carry enough # information to sensibly resolve to strategies at runtime. # Finally, we run a variation of the subclass lookup in `st.from_type` # among generic types in the lookup. # # Under 3.6 Union is handled directly in st.from_type, as the argument is # not an instance of `type`. However, under Python 3.5 Union *is* a type # and we have to handle it here, including failing if it has no parameters. if hasattr(thing, "__union_params__"): # pragma: no cover args = sorted(thing.__union_params__ or (), key=type_sorting_key) if not args: raise ResolutionFailed("Cannot resolve Union of no types.") return st.one_of([st.from_type(t) for t in args]) if getattr(thing, "__origin__", None) == tuple or isinstance( thing, getattr(typing, "TupleMeta", ())): elem_types = getattr(thing, "__tuple_params__", None) or () elem_types += getattr(thing, "__args__", None) or () if (getattr(thing, "__tuple_use_ellipsis__", False) or len(elem_types) == 2 and elem_types[-1] is Ellipsis): return st.lists(st.from_type(elem_types[0])).map(tuple) elif len(elem_types) == 1 and elem_types[0] == (): return st.tuples() # Empty tuple; see issue #1583 return st.tuples(*map(st.from_type, elem_types)) if (hasattr(typing, "Final") and getattr(thing, "__origin__", None) == typing.Final): # pragma: no cover # new in Python 3.8 return st.one_of([st.from_type(t) for t in thing.__args__]) if is_typing_literal(thing): # pragma: no cover # new in Python 3.8 args_dfs_stack = list(thing.__args__) literals = [] while args_dfs_stack: arg = args_dfs_stack.pop() if is_typing_literal(arg): args_dfs_stack.extend(reversed(arg.__args__)) else: literals.append(arg) return st.sampled_from(literals) # Now, confirm that we're dealing with a generic type as we expected if not isinstance(thing, typing_root_type): # pragma: no cover raise ResolutionFailed("Cannot resolve %s to a strategy" % (thing, )) # Some "generic" classes are not generic *in* anything - for example both # Hashable and Sized have `__args__ == ()` on Python 3.7 or later. # (In 3.6 they're just aliases for the collections.abc classes) origin = getattr(thing, "__origin__", thing) if (typing.Hashable is not collections.abc.Hashable and origin in vars(collections.abc).values() and len(getattr(thing, "__args__", None) or []) == 0 ): # pragma: no cover # impossible on 3.6 where we measure coverage. return st.from_type(origin) # Parametrised generic types have their __origin__ attribute set to the # un-parametrised version, which we need to use in the subclass checks. # e.g.: typing.List[int].__origin__ == typing.List mapping = { k: v for k, v in _global_type_lookup.items() if is_generic_type(k) and try_issubclass(k, thing) } if typing.Dict in mapping: # The subtype relationships between generic and concrete View types # are sometimes inconsistent under Python 3.5, so we pop them out to # preserve our invariant that all examples of from_type(T) are # instances of type T - and simplify the strategy for abstract types # such as Container for t in (typing.KeysView, typing.ValuesView, typing.ItemsView): mapping.pop(t, None) if len(mapping) > 1: # issubclass treats bytestring as a kind of sequence, which it is, # but treating it as such breaks everything else when it is presumed # to be a generic sequence or container that could hold any item. # Except for sequences of integers, or unions which include integer! # See https://github.com/HypothesisWorks/hypothesis/issues/2257 # # This block drops ByteString from the types that can be generated # if there is more than one allowed type, and the element type is # not either `int` or a Union with `int` as one of its elements. elem_type = (getattr(thing, "__args__", None) or ["not int"])[0] if getattr(elem_type, "__origin__", None) is typing.Union: union_elems = elem_type.__args__ elif hasattr(elem_type, "__union_params__"): # pragma: no cover union_elems = elem_type.__union_params__ # python 3.5 only else: union_elems = () if not any( isinstance(T, type) and issubclass(int, T) for T in list(union_elems) + [elem_type]): mapping.pop(typing.ByteString, None) strategies = [ v if isinstance(v, st.SearchStrategy) else v(thing) for k, v in mapping.items() if sum( try_issubclass(k, T) for T in mapping) == 1 ] empty = ", ".join(repr(s) for s in strategies if s.is_empty) if empty or not strategies: raise ResolutionFailed( "Could not resolve %s to a strategy; consider using " "register_type_strategy" % (empty or thing, )) return st.one_of(strategies)
def from_typing_type(thing): # We start with special-case support for Union and Tuple - the latter # isn't actually a generic type. Then we handle Literal since it doesn't # support `isinstance`. Support for Callable may be added to this section # later. # We then explicitly error on non-Generic types, which don't carry enough # information to sensibly resolve to strategies at runtime. # Finally, we run a variation of the subclass lookup in st.from_type # among generic types in the lookup. import typing # Under 3.6 Union is handled directly in st.from_type, as the argument is # not an instance of `type`. However, under Python 3.5 Union *is* a type # and we have to handle it here, including failing if it has no parameters. if hasattr(thing, "__union_params__"): # pragma: no cover args = sorted(thing.__union_params__ or (), key=type_sorting_key) if not args: raise ResolutionFailed("Cannot resolve Union of no types.") return st.one_of([st.from_type(t) for t in args]) if getattr(thing, "__origin__", None) == tuple or isinstance( thing, getattr(typing, "TupleMeta", ()) ): elem_types = getattr(thing, "__tuple_params__", None) or () elem_types += getattr(thing, "__args__", None) or () if ( getattr(thing, "__tuple_use_ellipsis__", False) or len(elem_types) == 2 and elem_types[-1] is Ellipsis ): return st.lists(st.from_type(elem_types[0])).map(tuple) elif len(elem_types) == 1 and elem_types[0] == (): return st.tuples() # Empty tuple; see issue #1583 return st.tuples(*map(st.from_type, elem_types)) if ( hasattr(typing, "Final") and getattr(thing, "__origin__", None) == typing.Final ): # pragma: no cover # new in Python 3.8 return st.one_of([st.from_type(t) for t in thing.__args__]) if is_typing_literal(thing): # pragma: no cover # new in Python 3.8 args_dfs_stack = list(thing.__args__) literals = [] while args_dfs_stack: arg = args_dfs_stack.pop() if is_typing_literal(arg): args_dfs_stack.extend(reversed(arg.__args__)) else: literals.append(arg) return st.sampled_from(literals) if isinstance(thing, typing.TypeVar): if getattr(thing, "__bound__", None) is not None: strat = unwrap_strategies(st.from_type(thing.__bound__)) if not isinstance(strat, OneOfStrategy): return strat # The bound was a union, or we resolved it as a union of subtypes, # so we need to unpack the strategy to ensure consistency across uses. # This incantation runs a sampled_from over the strategies inferred for # each part of the union, wraps that in shared so that we only generate # from one type per testcase, and flatmaps that back to instances. return st.shared( st.sampled_from(strat.original_strategies), key="typevar=%r" % (thing,) ).flatmap(lambda s: s) if getattr(thing, "__constraints__", None): return st.shared( st.sampled_from(thing.__constraints__), key="typevar=%r" % (thing,) ).flatmap(st.from_type) # Constraints may be None or () on various Python versions. return st.text() # An arbitrary type for the typevar # Now, confirm that we're dealing with a generic type as we expected if not isinstance(thing, typing_root_type): # pragma: no cover raise ResolutionFailed("Cannot resolve %s to a strategy" % (thing,)) # Parametrised generic types have their __origin__ attribute set to the # un-parametrised version, which we need to use in the subclass checks. # e.g.: typing.List[int].__origin__ == typing.List mapping = { k: v for k, v in _global_type_lookup.items() if isinstance(k, typing_root_type) and try_issubclass(k, thing) } if typing.Dict in mapping: # The subtype relationships between generic and concrete View types # are sometimes inconsistent under Python 3.5, so we pop them out to # preserve our invariant that all examples of from_type(T) are # instances of type T - and simplify the strategy for abstract types # such as Container for t in (typing.KeysView, typing.ValuesView, typing.ItemsView): mapping.pop(t, None) strategies = [ v if isinstance(v, st.SearchStrategy) else v(thing) for k, v in mapping.items() if sum(try_issubclass(k, T) for T in mapping) == 1 ] empty = ", ".join(repr(s) for s in strategies if s.is_empty) if empty or not strategies: # pragma: no cover raise ResolutionFailed( "Could not resolve %s to a strategy; consider using " "register_type_strategy" % (empty or thing,) ) return st.one_of(strategies)