189 lines
6.6 KiB
Python
189 lines
6.6 KiB
Python
# This file is part of Hypothesis, which may be found at
|
|
# https://github.com/HypothesisWorks/hypothesis/
|
|
#
|
|
# Most of this work is copyright (C) 2013-2021 David R. MacIver
|
|
# (david@drmaciver.com), but it contains contributions by others. See
|
|
# CONTRIBUTING.rst for a full list of people who may hold copyright, and
|
|
# consult the git log if you need to determine who owns an individual
|
|
# contribution.
|
|
#
|
|
# This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
# obtain one at https://mozilla.org/MPL/2.0/.
|
|
#
|
|
# END HEADER
|
|
|
|
import codecs
|
|
import inspect
|
|
import platform
|
|
import sys
|
|
import typing
|
|
|
|
PYPY = platform.python_implementation() == "PyPy"
|
|
WINDOWS = platform.system() == "Windows"
|
|
|
|
|
|
def escape_unicode_characters(s):
|
|
return codecs.encode(s, "unicode_escape").decode("ascii")
|
|
|
|
|
|
def int_from_bytes(data):
|
|
return int.from_bytes(data, "big")
|
|
|
|
|
|
def int_to_bytes(i, size):
|
|
return i.to_bytes(size, "big")
|
|
|
|
|
|
def int_to_byte(i):
|
|
return bytes([i])
|
|
|
|
|
|
try:
|
|
# These types are new in Python 3.7, but also (partially) backported to the
|
|
# typing backport on PyPI. Use if possible; or fall back to older names.
|
|
typing_root_type = (typing._Final, typing._GenericAlias) # type: ignore
|
|
ForwardRef = typing.ForwardRef # type: ignore
|
|
except AttributeError: # pragma: no cover
|
|
typing_root_type = (typing.TypingMeta, typing.TypeVar) # type: ignore
|
|
try:
|
|
typing_root_type += (typing._Union,) # type: ignore
|
|
except AttributeError:
|
|
pass
|
|
ForwardRef = typing._ForwardRef # type: ignore
|
|
|
|
|
|
def is_typed_named_tuple(cls):
|
|
"""Return True if cls is probably a subtype of `typing.NamedTuple`.
|
|
|
|
Unfortunately types created with `class T(NamedTuple):` actually
|
|
subclass `tuple` directly rather than NamedTuple. This is annoying,
|
|
and means we just have to hope that nobody defines a different tuple
|
|
subclass with similar attributes.
|
|
"""
|
|
return (
|
|
issubclass(cls, tuple)
|
|
and hasattr(cls, "_fields")
|
|
and (hasattr(cls, "_field_types") or hasattr(cls, "__annotations__"))
|
|
)
|
|
|
|
|
|
def get_type_hints(thing):
|
|
"""Like the typing version, but tries harder and never errors.
|
|
|
|
Tries harder: if the thing to inspect is a class but typing.get_type_hints
|
|
raises an error or returns no hints, then this function will try calling it
|
|
on the __init__ method. This second step often helps with user-defined
|
|
classes on older versions of Python. The third step we take is trying
|
|
to fetch types from the __signature__ property.
|
|
They override any other ones we found earlier.
|
|
|
|
Never errors: instead of raising TypeError for uninspectable objects, or
|
|
NameError for unresolvable forward references, just return an empty dict.
|
|
"""
|
|
kwargs = {} if sys.version_info[:2] < (3, 9) else {"include_extras": True}
|
|
|
|
try:
|
|
hints = typing.get_type_hints(thing, **kwargs)
|
|
except (AttributeError, TypeError, NameError):
|
|
hints = {}
|
|
|
|
if inspect.isclass(thing):
|
|
try:
|
|
hints.update(typing.get_type_hints(thing.__init__, **kwargs))
|
|
except (TypeError, NameError, AttributeError):
|
|
pass
|
|
|
|
try:
|
|
if hasattr(thing, "__signature__"):
|
|
# It is possible for the signature and annotations attributes to
|
|
# differ on an object due to renamed arguments.
|
|
from hypothesis.strategies._internal.types import is_a_type
|
|
|
|
vkinds = (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD)
|
|
for p in inspect.signature(thing).parameters.values():
|
|
if p.kind not in vkinds and is_a_type(p.annotation):
|
|
if p.default is None:
|
|
hints[p.name] = typing.Optional[p.annotation]
|
|
else:
|
|
hints[p.name] = p.annotation
|
|
except (AttributeError, TypeError, NameError): # pragma: no cover
|
|
pass
|
|
|
|
return hints
|
|
|
|
|
|
def update_code_location(code, newfile, newlineno):
|
|
"""Take a code object and lie shamelessly about where it comes from.
|
|
|
|
Why do we want to do this? It's for really shallow reasons involving
|
|
hiding the hypothesis_temporary_module code from test runners like
|
|
pytest's verbose mode. This is a vastly disproportionate terrible
|
|
hack that I've done purely for vanity, and if you're reading this
|
|
code you're probably here because it's broken something and now
|
|
you're angry at me. Sorry.
|
|
"""
|
|
if hasattr(code, "replace"):
|
|
# Python 3.8 added positional-only params (PEP 570), and thus changed
|
|
# the layout of code objects. In beta1, the `.replace()` method was
|
|
# added to facilitate future-proof code. See BPO-37032 for details.
|
|
return code.replace(co_filename=newfile, co_firstlineno=newlineno)
|
|
|
|
else: # pragma: no cover
|
|
# This field order is accurate for 3.5 - 3.7, but not 3.8 when a new field
|
|
# was added for positional-only arguments. However it also added a .replace()
|
|
# method that we use instead of field indices, so they're fine as-is.
|
|
CODE_FIELD_ORDER = [
|
|
"co_argcount",
|
|
"co_kwonlyargcount",
|
|
"co_nlocals",
|
|
"co_stacksize",
|
|
"co_flags",
|
|
"co_code",
|
|
"co_consts",
|
|
"co_names",
|
|
"co_varnames",
|
|
"co_filename",
|
|
"co_name",
|
|
"co_firstlineno",
|
|
"co_lnotab",
|
|
"co_freevars",
|
|
"co_cellvars",
|
|
]
|
|
unpacked = [getattr(code, name) for name in CODE_FIELD_ORDER]
|
|
unpacked[CODE_FIELD_ORDER.index("co_filename")] = newfile
|
|
unpacked[CODE_FIELD_ORDER.index("co_firstlineno")] = newlineno
|
|
return type(code)(*unpacked)
|
|
|
|
|
|
# Under Python 2, math.floor and math.ceil returned floats, which cannot
|
|
# represent large integers - eg `float(2**53) == float(2**53 + 1)`.
|
|
# We therefore implement them entirely in (long) integer operations.
|
|
# We still use the same trick on Python 3, because Numpy values and other
|
|
# custom __floor__ or __ceil__ methods may convert via floats.
|
|
# See issue #1667, Numpy issue 9068.
|
|
def floor(x):
|
|
y = int(x)
|
|
if y != x and x < 0:
|
|
return y - 1
|
|
return y
|
|
|
|
|
|
def ceil(x):
|
|
y = int(x)
|
|
if y != x and x > 0:
|
|
return y + 1
|
|
return y
|
|
|
|
|
|
def bad_django_TestCase(runner):
|
|
if runner is None or "django.test" not in sys.modules:
|
|
return False
|
|
else: # pragma: no cover
|
|
if not isinstance(runner, sys.modules["django.test"].TransactionTestCase):
|
|
return False
|
|
|
|
from hypothesis.extra.django._impl import HypothesisTestCase
|
|
|
|
return not isinstance(runner, HypothesisTestCase)
|