def _imports_for_strategy(strategy): # If we have a lazy from_type strategy, because unwrapping it gives us an # error or invalid syntax, import that type and we're done. if isinstance(strategy, LazyStrategy) and strategy.function is st.from_type: return _imports_for_object(strategy._LazyStrategy__args[0]) imports = set() strategy = unwrap_strategies(strategy) # Get imports for s.map(f), s.filter(f), s.flatmap(f), including both s and f if isinstance(strategy, MappedSearchStrategy): imports |= _imports_for_strategy(strategy.mapped_strategy) imports |= _imports_for_object(strategy.pack) if isinstance(strategy, FilteredStrategy): imports |= _imports_for_strategy(strategy.filtered_strategy) for f in strategy.flat_conditions: imports |= _imports_for_object(f) if isinstance(strategy, FlatMapStrategy): imports |= _imports_for_strategy(strategy.flatmapped_strategy) imports |= _imports_for_object(strategy.expand) # recurse through one_of to handle e.g. from_type(Optional[Foo]) if isinstance(strategy, OneOfStrategy): for s in strategy.element_strategies: imports |= _imports_for_strategy(s) # get imports for the target of builds(), and recurse into the argument strategies if isinstance(strategy, BuildsStrategy): imports |= _imports_for_object(strategy.target) for s in strategy.args: imports |= _imports_for_strategy(s) for s in strategy.kwargs.values(): imports |= _imports_for_strategy(s) return imports
def resolve_TypeVar(thing): type_var_key = f"typevar={thing!r}" if getattr(thing, "__bound__", None) is not None: bound = thing.__bound__ if isinstance(bound, ForwardRef): bound = _try_import_forward_ref(thing, bound) strat = unwrap_strategies(st.from_type(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=type_var_key).flatmap(lambda s: s) builtin_scalar_types = [type(None), bool, int, float, str, bytes] return st.shared( st.sampled_from( # Constraints may be None or () on various Python versions. getattr(thing, "__constraints__", None) or builtin_scalar_types, ), key=type_var_key, ).flatmap(st.from_type)
def test_sequence_filter_rewriting(strategy, predicate): s = unwrap_strategies(strategy) fs = s.filter(predicate) assert not isinstance(fs, FilteredStrategy) if s.min_size > 0: assert fs is s else: assert fs.min_size == 1
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. # # 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, )) # 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 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) 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: # 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 test_identity_map_is_noop(): s = unwrap_strategies(st.integers()) assert s.map(lambda x: x) is s
def test_bumps_min_size_and_filters_for_content_str_methods(method): s = unwrap_strategies(st.text()) fs = s.filter(method) assert fs.filtered_strategy.min_size == 1 assert fs.flat_conditions == (method, )
def test_warns_on_suspicious_string_methods(method): s = unwrap_strategies(st.text()) with pytest.warns(HypothesisWarning, match="this allows all nonempty strings! Did you mean"): fs = s.filter(method) assert fs.min_size == 1