Merge remote-tracking branch 'upstream/features' into invocation-scoped-fixtures

This commit is contained in:
Bruno Oliveira 2016-07-11 20:04:23 -03:00
commit fb4da00a32
9 changed files with 1391 additions and 1318 deletions

203
_pytest/compat.py Normal file
View File

@ -0,0 +1,203 @@
"""
python version compatibility code
"""
import sys
import inspect
import types
import re
import functools
import py
import _pytest
try:
import enum
except ImportError: # pragma: no cover
# Only available in Python 3.4+ or as a backport
enum = None
_PY3 = sys.version_info > (3, 0)
_PY2 = not _PY3
NoneType = type(None)
NOTSET = object()
if hasattr(inspect, 'signature'):
def _format_args(func):
return str(inspect.signature(func))
else:
def _format_args(func):
return inspect.formatargspec(*inspect.getargspec(func))
isfunction = inspect.isfunction
isclass = inspect.isclass
# used to work around a python2 exception info leak
exc_clear = getattr(sys, 'exc_clear', lambda: None)
# The type of re.compile objects is not exposed in Python.
REGEX_TYPE = type(re.compile(''))
def is_generator(func):
try:
return _pytest._code.getrawcode(func).co_flags & 32 # generator function
except AttributeError: # builtin functions have no bytecode
# assume them to not be generators
return False
def getlocation(function, curdir):
import inspect
fn = py.path.local(inspect.getfile(function))
lineno = py.builtin._getcode(function).co_firstlineno
if fn.relto(curdir):
fn = fn.relto(curdir)
return "%s:%d" %(fn, lineno+1)
def num_mock_patch_args(function):
""" return number of arguments used up by mock arguments (if any) """
patchings = getattr(function, "patchings", None)
if not patchings:
return 0
mock = sys.modules.get("mock", sys.modules.get("unittest.mock", None))
if mock is not None:
return len([p for p in patchings
if not p.attribute_name and p.new is mock.DEFAULT])
return len(patchings)
def getfuncargnames(function, startindex=None):
# XXX merge with main.py's varnames
#assert not isclass(function)
realfunction = function
while hasattr(realfunction, "__wrapped__"):
realfunction = realfunction.__wrapped__
if startindex is None:
startindex = inspect.ismethod(function) and 1 or 0
if realfunction != function:
startindex += num_mock_patch_args(function)
function = realfunction
if isinstance(function, functools.partial):
argnames = inspect.getargs(_pytest._code.getrawcode(function.func))[0]
partial = function
argnames = argnames[len(partial.args):]
if partial.keywords:
for kw in partial.keywords:
argnames.remove(kw)
else:
argnames = inspect.getargs(_pytest._code.getrawcode(function))[0]
defaults = getattr(function, 'func_defaults',
getattr(function, '__defaults__', None)) or ()
numdefaults = len(defaults)
if numdefaults:
return tuple(argnames[startindex:-numdefaults])
return tuple(argnames[startindex:])
if sys.version_info[:2] == (2, 6):
def isclass(object):
""" Return true if the object is a class. Overrides inspect.isclass for
python 2.6 because it will return True for objects which always return
something on __getattr__ calls (see #1035).
Backport of https://hg.python.org/cpython/rev/35bf8f7a8edc
"""
return isinstance(object, (type, types.ClassType))
if _PY3:
import codecs
STRING_TYPES = bytes, str
def _escape_strings(val):
"""If val is pure ascii, returns it as a str(). Otherwise, escapes
bytes objects into a sequence of escaped bytes:
b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6'
and escapes unicode objects into a sequence of escaped unicode
ids, e.g.:
'4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944'
note:
the obvious "v.decode('unicode-escape')" will return
valid utf-8 unicode if it finds them in bytes, but we
want to return escaped bytes for any byte, even if they match
a utf-8 string.
"""
if isinstance(val, bytes):
if val:
# source: http://goo.gl/bGsnwC
encoded_bytes, _ = codecs.escape_encode(val)
return encoded_bytes.decode('ascii')
else:
# empty bytes crashes codecs.escape_encode (#1087)
return ''
else:
return val.encode('unicode_escape').decode('ascii')
else:
STRING_TYPES = bytes, str, unicode
def _escape_strings(val):
"""In py2 bytes and str are the same type, so return if it's a bytes
object, return it unchanged if it is a full ascii string,
otherwise escape it into its binary form.
If it's a unicode string, change the unicode characters into
unicode escapes.
"""
if isinstance(val, bytes):
try:
return val.encode('ascii')
except UnicodeDecodeError:
return val.encode('string-escape')
else:
return val.encode('unicode-escape')
def get_real_func(obj):
""" gets the real function object of the (possibly) wrapped object by
functools.wraps or functools.partial.
"""
while hasattr(obj, "__wrapped__"):
obj = obj.__wrapped__
if isinstance(obj, functools.partial):
obj = obj.func
return obj
def getfslineno(obj):
# xxx let decorators etc specify a sane ordering
obj = get_real_func(obj)
if hasattr(obj, 'place_as'):
obj = obj.place_as
fslineno = _pytest._code.getfslineno(obj)
assert isinstance(fslineno[1], int), obj
return fslineno
def getimfunc(func):
try:
return func.__func__
except AttributeError:
try:
return func.im_func
except AttributeError:
return func
def safe_getattr(object, name, default):
""" Like getattr but return default upon any Exception.
Attribute access can potentially fail for 'evil' Python objects.
See issue214
"""
try:
return getattr(object, name, default)
except Exception:
return default

View File

@ -63,7 +63,7 @@ class UsageError(Exception):
_preinit = []
default_plugins = (
"mark main terminal runner python debugging unittest capture skipping "
"mark main terminal runner python fixtures debugging unittest capture skipping "
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion "
"junitxml resultlog doctest cacheprovider freeze_support "
"setuponly setupplan").split()

View File

@ -5,7 +5,7 @@ import traceback
import pytest
from _pytest._code.code import TerminalRepr, ReprFileLocation, ExceptionInfo
from _pytest.python import FixtureRequest
from _pytest.fixtures import FixtureRequest

1126
_pytest/fixtures.py Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -334,7 +334,7 @@ class TestFunction:
reprec.assertoutcome()
def test_function_equality(self, testdir, tmpdir):
from _pytest.python import FixtureManager
from _pytest.fixtures import FixtureManager
config = testdir.parseconfigure()
session = testdir.Session(config)
session._fixturemanager = FixtureManager(session)

View File

@ -3,31 +3,30 @@ from textwrap import dedent
import _pytest._code
import pytest
import sys
from _pytest import python as funcargs
from _pytest.pytester import get_public_names
from _pytest.python import FixtureLookupError
from _pytest.fixtures import FixtureLookupError
from _pytest import fixtures
def test_getfuncargnames():
def f(): pass
assert not funcargs.getfuncargnames(f)
assert not fixtures.getfuncargnames(f)
def g(arg): pass
assert funcargs.getfuncargnames(g) == ('arg',)
assert fixtures.getfuncargnames(g) == ('arg',)
def h(arg1, arg2="hello"): pass
assert funcargs.getfuncargnames(h) == ('arg1',)
assert fixtures.getfuncargnames(h) == ('arg1',)
def h(arg1, arg2, arg3="hello"): pass
assert funcargs.getfuncargnames(h) == ('arg1', 'arg2')
assert fixtures.getfuncargnames(h) == ('arg1', 'arg2')
class A:
def f(self, arg1, arg2="hello"):
pass
assert funcargs.getfuncargnames(A().f) == ('arg1',)
assert fixtures.getfuncargnames(A().f) == ('arg1',)
if sys.version_info < (3,0):
assert funcargs.getfuncargnames(A.f) == ('arg1',)
assert fixtures.getfuncargnames(A.f) == ('arg1',)
class TestFillFixtures:
def test_fillfuncargs_exposed(self):
# used by oejskit, kept for compatibility
assert pytest._fillfuncargs == funcargs.fillfixtures
assert pytest._fillfuncargs == fixtures.fillfixtures
def test_funcarg_lookupfails(self, testdir):
testdir.makepyfile("""
@ -54,7 +53,7 @@ class TestFillFixtures:
def test_func(some, other):
pass
""")
funcargs.fillfixtures(item)
fixtures.fillfixtures(item)
del item.funcargs["request"]
assert len(get_public_names(item.funcargs)) == 2
assert item.funcargs['some'] == "test_func"
@ -400,7 +399,7 @@ class TestRequestBasic:
def pytest_funcarg__something(request): pass
def test_func(something): pass
""")
req = funcargs.FixtureRequest(item)
req = fixtures.FixtureRequest(item)
assert req.function == item.obj
assert req.keywords == item.keywords
assert hasattr(req.module, 'test_func')
@ -431,7 +430,7 @@ class TestRequestBasic:
""")
item1, = testdir.genitems([modcol])
assert item1.name == "test_method"
arg2fixturedefs = funcargs.FixtureRequest(item1)._arg2fixturedefs
arg2fixturedefs = fixtures.FixtureRequest(item1)._arg2fixturedefs
assert len(arg2fixturedefs) == 1
assert arg2fixturedefs[0].__name__ == "pytest_funcarg__something"
@ -558,7 +557,7 @@ class TestRequestBasic:
def test_request_getmodulepath(self, testdir):
modcol = testdir.getmodulecol("def test_somefunc(): pass")
item, = testdir.genitems([modcol])
req = funcargs.FixtureRequest(item)
req = fixtures.FixtureRequest(item)
assert req.fspath == modcol.fspath
def test_request_fixturenames(self, testdir):
@ -687,7 +686,7 @@ class TestRequestMarking:
def test_func2(self, something):
pass
""")
req1 = funcargs.FixtureRequest(item1)
req1 = fixtures.FixtureRequest(item1)
assert 'xfail' not in item1.keywords
req1.applymarker(pytest.mark.xfail)
assert 'xfail' in item1.keywords
@ -769,7 +768,7 @@ class TestRequestCachedSetup:
def test_request_cachedsetup_extrakey(self, testdir):
item1 = testdir.getitem("def test_func(): pass")
req1 = funcargs.FixtureRequest(item1)
req1 = fixtures.FixtureRequest(item1)
l = ["hello", "world"]
def setup():
return l.pop()
@ -784,7 +783,7 @@ class TestRequestCachedSetup:
def test_request_cachedsetup_cache_deletion(self, testdir):
item1 = testdir.getitem("def test_func(): pass")
req1 = funcargs.FixtureRequest(item1)
req1 = fixtures.FixtureRequest(item1)
l = []
def setup():
l.append("setup")

View File

@ -73,7 +73,7 @@ def test_wrapped_getfslineno():
class TestMockDecoration:
def test_wrapped_getfuncargnames(self):
from _pytest.python import getfuncargnames
from _pytest.compat import getfuncargnames
def wrap(f):
def func():
pass
@ -86,7 +86,7 @@ class TestMockDecoration:
assert l == ("x",)
def test_wrapped_getfuncargnames_patching(self):
from _pytest.python import getfuncargnames
from _pytest.compat import getfuncargnames
def wrap(f):
def func():
pass
@ -234,7 +234,7 @@ class TestReRunTests:
""")
def test_pytestconfig_is_session_scoped():
from _pytest.python import pytestconfig
from _pytest.fixtures import pytestconfig
assert pytestconfig._pytestfixturefunction.scope == "session"

View File

@ -5,7 +5,7 @@ import sys
import _pytest._code
import py
import pytest
from _pytest import python as funcargs
from _pytest import python, fixtures
import hypothesis
from hypothesis import strategies
@ -22,9 +22,9 @@ class TestMetafunc:
name2fixturedefs = None
def __init__(self, names):
self.names_closure = names
names = funcargs.getfuncargnames(func)
names = fixtures.getfuncargnames(func)
fixtureinfo = FixtureInfo(names)
return funcargs.Metafunc(func, fixtureinfo, None)
return python.Metafunc(func, fixtureinfo, None)
def test_no_funcargs(self, testdir):
def function(): pass
@ -558,16 +558,16 @@ class TestMetafunc:
def test_format_args(self):
def function1(): pass
assert funcargs._format_args(function1) == '()'
assert fixtures._format_args(function1) == '()'
def function2(arg1): pass
assert funcargs._format_args(function2) == "(arg1)"
assert fixtures._format_args(function2) == "(arg1)"
def function3(arg1, arg2="qwe"): pass
assert funcargs._format_args(function3) == "(arg1, arg2='qwe')"
assert fixtures._format_args(function3) == "(arg1, arg2='qwe')"
def function4(arg1, *args, **kwargs): pass
assert funcargs._format_args(function4) == "(arg1, *args, **kwargs)"
assert fixtures._format_args(function4) == "(arg1, *args, **kwargs)"
class TestMetafuncFunctional: