merge with issue-commits

--HG--
branch : trunk
This commit is contained in:
holger krekel 2010-05-12 10:56:37 +02:00
commit 3d70917758
15 changed files with 88 additions and 194 deletions

View File

@ -1,3 +1,21 @@
Changes between 1.3.0 and 1.3.1
==================================================
- make py.test.cmdline.main() return the exitstatus
instead of raising (which is still done by py.cmdline.pytest())
and make it so that py.test.cmdline.main() can be called
multiple times, at least as far as py.test's internal
state is concerned - previously it would raise an exception
on the second time.
- improve tracebacks presentation:
- raises shows shorter more relevant tracebacks
- improve support for raises and other dynamically compiled code by
manipulating python's linecache.cache instead of the previous
rather hacky way of creating custom code objects. This makes
it seemlessly work on Jython and PyPy at least.
Changes between 1.2.1 and 1.3.0 Changes between 1.2.1 and 1.3.0
================================================== ==================================================

View File

@ -2,6 +2,9 @@
this little helper allows to run tests multiple times this little helper allows to run tests multiple times
in the same process. useful for running tests from in the same process. useful for running tests from
a console. a console.
NOTE: since 1.3.1 you can just call py.test.cmdline.main()
multiple times - no special logic needed.
""" """
import py, sys import py, sys

View File

@ -8,7 +8,7 @@ dictionary or an import path.
(c) Holger Krekel and others, 2004-2010 (c) Holger Krekel and others, 2004-2010
""" """
__version__ = version = "1.3.0" __version__ = version = "1.3.1a1"
import py.apipkg import py.apipkg
@ -25,7 +25,6 @@ py.apipkg.initpkg(__name__, dict(
'pytest': '._cmdline.pytest:main', 'pytest': '._cmdline.pytest:main',
'pylookup': '._cmdline.pylookup:main', 'pylookup': '._cmdline.pylookup:main',
'pycountloc': '._cmdline.pycountlog:main', 'pycountloc': '._cmdline.pycountlog:main',
'pytest': '._test.cmdline:main',
'pylookup': '._cmdline.pylookup:main', 'pylookup': '._cmdline.pylookup:main',
'pycountloc': '._cmdline.pycountloc:main', 'pycountloc': '._cmdline.pycountloc:main',
'pycleanup': '._cmdline.pycleanup:main', 'pycleanup': '._cmdline.pycleanup:main',

View File

@ -151,6 +151,9 @@ else:
return getattr(function, "__dict__", None) return getattr(function, "__dict__", None)
def _getcode(function): def _getcode(function):
try:
return getattr(function, "__code__")
except AttributeError:
return getattr(function, "func_code", None) return getattr(function, "func_code", None)
def print_(*args, **kwargs): def print_(*args, **kwargs):
@ -175,6 +178,7 @@ else:
def exec_(obj, globals=None, locals=None): def exec_(obj, globals=None, locals=None):
""" minimal backport of py3k exec statement. """ """ minimal backport of py3k exec statement. """
__tracebackhide__ = True
if globals is None: if globals is None:
frame = sys._getframe(1) frame = sys._getframe(1)
globals = frame.f_globals globals = frame.f_globals
@ -187,14 +191,17 @@ else:
if sys.version_info >= (3,0): if sys.version_info >= (3,0):
exec (""" exec ("""
def _reraise(cls, val, tb): def _reraise(cls, val, tb):
__tracebackhide__ = True
assert hasattr(val, '__traceback__') assert hasattr(val, '__traceback__')
raise val raise val
""") """)
else: else:
exec (""" exec ("""
def _reraise(cls, val, tb): def _reraise(cls, val, tb):
__tracebackhide__ = True
raise cls, val, tb raise cls, val, tb
def exec2(obj, globals, locals): def exec2(obj, globals, locals):
__tracebackhide__ = True
exec obj in globals, locals exec obj in globals, locals
""") """)

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
import py import py
def main(args): def main(args=None):
py.test.cmdline.main(args) raise SystemExit(py.test.cmdline.main(args))

View File

@ -23,58 +23,8 @@ class Code(object):
def __ne__(self, other): def __ne__(self, other):
return not self == other return not self == other
def new(self, rec=False, **kwargs):
""" return new code object with modified attributes.
if rec-cursive is true then dive into code
objects contained in co_consts.
"""
if sys.platform.startswith("java"):
# XXX jython does not support the below co_filename hack
return self.raw
names = [x for x in dir(self.raw) if x[:3] == 'co_']
for name in kwargs:
if name not in names:
raise TypeError("unknown code attribute: %r" %(name, ))
if rec and hasattr(self.raw, 'co_consts'): # jython
newconstlist = []
co = self.raw
cotype = type(co)
for c in co.co_consts:
if isinstance(c, cotype):
c = self.__class__(c).new(rec=True, **kwargs)
newconstlist.append(c)
return self.new(rec=False, co_consts=tuple(newconstlist), **kwargs)
for name in names:
if name not in kwargs:
kwargs[name] = getattr(self.raw, name)
arglist = [
kwargs['co_argcount'],
kwargs['co_nlocals'],
kwargs.get('co_stacksize', 0), # jython
kwargs.get('co_flags', 0), # jython
kwargs.get('co_code', ''), # jython
kwargs.get('co_consts', ()), # jython
kwargs.get('co_names', []), #
kwargs['co_varnames'],
kwargs['co_filename'],
kwargs['co_name'],
kwargs['co_firstlineno'],
kwargs.get('co_lnotab', ''), #jython
kwargs.get('co_freevars', None), #jython
kwargs.get('co_cellvars', None), # jython
]
if sys.version_info >= (3,0):
arglist.insert(1, kwargs['co_kwonlyargcount'])
return self.raw.__class__(*arglist)
else:
return py.std.new.code(*arglist)
def path(self): def path(self):
""" return a path object pointing to source code""" """ return a path object pointing to source code"""
fn = self.raw.co_filename
try:
return fn.__path__
except AttributeError:
p = py.path.local(self.raw.co_filename) p = py.path.local(self.raw.co_filename)
if not p.check(): if not p.check():
# XXX maybe try harder like the weird logic # XXX maybe try harder like the weird logic

View File

@ -212,10 +212,10 @@ class Source(object):
else: else:
if flag & _AST_FLAG: if flag & _AST_FLAG:
return co return co
co_filename = MyStr(filename) from types import ModuleType
co_filename.__source__ = self lines = [(x + "\n") for x in self.lines]
return py.code.Code(co).new(rec=1, co_filename=co_filename) py.std.linecache.cache[filename] = (1, None, lines, filename)
#return newcode_withfilename(co, co_filename) return co
# #
# public API shortcut functions # public API shortcut functions
@ -224,11 +224,9 @@ class Source(object):
def compile_(source, filename=None, mode='exec', flags= def compile_(source, filename=None, mode='exec', flags=
generators.compiler_flag, dont_inherit=0): generators.compiler_flag, dont_inherit=0):
""" compile the given source to a raw code object, """ compile the given source to a raw code object,
which points back to the source code through and maintain an internal cache which allows later
"co_filename.__source__". All code objects retrieval of the source code for the code object
contained in the code object will recursively and any recursively created code objects.
also have this special subclass-of-string
filename.
""" """
if _ast is not None and isinstance(source, _ast.AST): if _ast is not None and isinstance(source, _ast.AST):
# XXX should Source support having AST? # XXX should Source support having AST?
@ -262,14 +260,8 @@ def getfslineno(obj):
# #
# helper functions # helper functions
# #
class MyStr(str):
""" custom string which allows to add attributes. """
def findsource(obj): def findsource(obj):
obj = py.code.getrawcode(obj)
try:
fullsource = obj.co_filename.__source__
except AttributeError:
try: try:
sourcelines, lineno = py.std.inspect.findsource(obj) sourcelines, lineno = py.std.inspect.findsource(obj)
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
@ -279,27 +271,15 @@ def findsource(obj):
source = Source() source = Source()
source.lines = [line.rstrip() for line in sourcelines] source.lines = [line.rstrip() for line in sourcelines]
return source, lineno return source, lineno
else:
lineno = obj.co_firstlineno - 1
return fullsource, lineno
def getsource(obj, **kwargs): def getsource(obj, **kwargs):
obj = py.code.getrawcode(obj) obj = py.code.getrawcode(obj)
try:
fullsource = obj.co_filename.__source__
except AttributeError:
try: try:
strsrc = inspect.getsource(obj) strsrc = inspect.getsource(obj)
except IndentationError: except IndentationError:
strsrc = "\"Buggy python version consider upgrading, cannot get source\"" strsrc = "\"Buggy python version consider upgrading, cannot get source\""
assert isinstance(strsrc, str) assert isinstance(strsrc, str)
return Source(strsrc, **kwargs) return Source(strsrc, **kwargs)
else:
lineno = obj.co_firstlineno - 1
end = fullsource.getblockend(lineno)
return Source(fullsource[lineno:end+1], deident=True)
def deindent(lines, offset=None): def deindent(lines, offset=None):
if offset is None: if offset is None:

View File

@ -16,8 +16,9 @@ def main(args=None):
colitems = config.getinitialnodes() colitems = config.getinitialnodes()
exitstatus = session.main(colitems) exitstatus = session.main(colitems)
config.pluginmanager.do_unconfigure(config) config.pluginmanager.do_unconfigure(config)
raise SystemExit(exitstatus)
except config.Error: except config.Error:
e = sys.exc_info()[1] e = sys.exc_info()[1]
sys.stderr.write("ERROR: %s\n" %(e.args[0],)) sys.stderr.write("ERROR: %s\n" %(e.args[0],))
raise SystemExit(3) exitstatus = 3
py.test.config = py.test.config.__class__()
return exitstatus

View File

@ -393,5 +393,5 @@ class Function(FunctionMixin, py.test.collect.Item):
def hasinit(obj): def hasinit(obj):
init = getattr(obj, '__init__', None) init = getattr(obj, '__init__', None)
if init: if init:
if not isinstance(init, type(object.__init__)): if init != object.__init__:
return True return True

View File

@ -26,7 +26,7 @@ def main():
name='py', name='py',
description='py.test and pylib: rapid testing and development utils.', description='py.test and pylib: rapid testing and development utils.',
long_description = long_description, long_description = long_description,
version= '1.3.0', version= '1.3.1a1',
url='http://pylib.org', url='http://pylib.org',
license='MIT license', license='MIT license',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],

View File

@ -89,3 +89,19 @@ class TestGeneralUsage:
assert result.ret == 0 assert result.ret == 0
s = result.stdout.str() s = result.stdout.str()
assert 'MarkGenerator' in s assert 'MarkGenerator' in s
def test_double_pytestcmdline(self, testdir):
p = testdir.makepyfile(run="""
import py
py.test.cmdline.main()
py.test.cmdline.main()
""")
testdir.makepyfile("""
def test_hello():
pass
""")
result = testdir.runpython(p)
result.stdout.fnmatch_lines([
"*1 passed*",
"*1 passed*",
])

View File

@ -1,87 +1,12 @@
from __future__ import generators
import py import py
import sys import sys
failsonjython = py.test.mark.xfail("sys.platform.startswith('java')")
def test_newcode():
source = "i = 3"
co = compile(source, '', 'exec')
code = py.code.Code(co)
newco = code.new()
assert co == newco
def test_ne(): def test_ne():
code1 = py.code.Code(compile('foo = "bar"', '', 'exec')) code1 = py.code.Code(compile('foo = "bar"', '', 'exec'))
assert code1 == code1 assert code1 == code1
code2 = py.code.Code(compile('foo = "baz"', '', 'exec')) code2 = py.code.Code(compile('foo = "baz"', '', 'exec'))
assert code2 != code1 assert code2 != code1
@failsonjython
def test_newcode_unknown_args():
code = py.code.Code(compile("", '', 'exec'))
py.test.raises(TypeError, 'code.new(filename="hello")')
@failsonjython
def test_newcode_withfilename():
source = py.code.Source("""
def f():
def g():
pass
""")
co = compile(str(source)+'\n', 'nada', 'exec')
obj = 'hello'
newco = py.code.Code(co).new(rec=True, co_filename=obj)
def walkcode(co):
for x in co.co_consts:
if isinstance(x, type(co)):
for y in walkcode(x):
yield y
yield co
names = []
for code in walkcode(newco):
assert newco.co_filename == obj
assert newco.co_filename is obj
names.append(code.co_name)
assert 'f' in names
assert 'g' in names
@failsonjython
def test_newcode_with_filename():
source = "i = 3"
co = compile(source, '', 'exec')
code = py.code.Code(co)
class MyStr(str):
pass
filename = MyStr("hello")
filename.__source__ = py.code.Source(source)
newco = code.new(rec=True, co_filename=filename)
assert newco.co_filename.__source__ == filename.__source__
s = py.code.Source(newco)
assert str(s) == source
@failsonjython
def test_new_code_object_carries_filename_through():
class mystr(str):
pass
filename = mystr("dummy")
co = compile("hello\n", filename, 'exec')
assert not isinstance(co.co_filename, mystr)
args = [
co.co_argcount, co.co_nlocals, co.co_stacksize,
co.co_flags, co.co_code, co.co_consts,
co.co_names, co.co_varnames,
filename,
co.co_name, co.co_firstlineno, co.co_lnotab,
co.co_freevars, co.co_cellvars
]
if sys.version_info > (3,0):
args.insert(1, co.co_kwonlyargcount)
c2 = py.std.types.CodeType(*args)
assert c2.co_filename is filename
def test_code_gives_back_name_for_not_existing_file(): def test_code_gives_back_name_for_not_existing_file():
name = 'abc-123' name = 'abc-123'
co_code = compile("pass\n", name, 'exec') co_code = compile("pass\n", name, 'exec')

View File

@ -293,7 +293,6 @@ class TestFormattedExcinfo:
assert lines[0] == "| def f(x):" assert lines[0] == "| def f(x):"
assert lines[1] == " pass" assert lines[1] == " pass"
@failsonjython
def test_repr_source_excinfo(self): def test_repr_source_excinfo(self):
""" check if indentation is right """ """ check if indentation is right """
pr = FormattedExcinfo() pr = FormattedExcinfo()

View File

@ -80,11 +80,10 @@ def test_source_strip_multiline():
source2 = source.strip() source2 = source.strip()
assert source2.lines == [" hello"] assert source2.lines == [" hello"]
@failsonjython
def test_syntaxerror_rerepresentation(): def test_syntaxerror_rerepresentation():
ex = py.test.raises(SyntaxError, py.code.compile, 'x x') ex = py.test.raises(SyntaxError, py.code.compile, 'xyz xyz')
assert ex.value.lineno == 1 assert ex.value.lineno == 1
assert ex.value.offset == 3 assert ex.value.offset in (4,7) # XXX pypy/jython versus cpython?
assert ex.value.text.strip(), 'x x' assert ex.value.text.strip(), 'x x'
def test_isparseable(): def test_isparseable():
@ -132,7 +131,6 @@ class TestSourceParsingAndCompiling:
exec (co, d) exec (co, d)
assert d['x'] == 3 assert d['x'] == 3
@failsonjython
def test_compile_and_getsource_simple(self): def test_compile_and_getsource_simple(self):
co = py.code.compile("x=3") co = py.code.compile("x=3")
exec (co) exec (co)
@ -203,7 +201,6 @@ class TestSourceParsingAndCompiling:
assert isinstance(mod, ast.Module) assert isinstance(mod, ast.Module)
compile(mod, "<filename>", "exec") compile(mod, "<filename>", "exec")
@failsonjython
def test_compile_and_getsource(self): def test_compile_and_getsource(self):
co = self.source.compile() co = self.source.compile()
py.builtin.exec_(co, globals()) py.builtin.exec_(co, globals())
@ -260,7 +257,6 @@ def test_getstartingblock_multiline():
l = [i for i in x.source.lines if i.strip()] l = [i for i in x.source.lines if i.strip()]
assert len(l) == 4 assert len(l) == 4
@failsonjython
def test_getline_finally(): def test_getline_finally():
def c(): pass def c(): pass
excinfo = py.test.raises(TypeError, """ excinfo = py.test.raises(TypeError, """
@ -274,7 +270,6 @@ def test_getline_finally():
source = excinfo.traceback[-1].statement source = excinfo.traceback[-1].statement
assert str(source).strip() == 'c(1)' assert str(source).strip() == 'c(1)'
@failsonjython
def test_getfuncsource_dynamic(): def test_getfuncsource_dynamic():
source = """ source = """
def f(): def f():
@ -341,7 +336,6 @@ def test_getsource_fallback():
src = getsource(x) src = getsource(x)
assert src == expected assert src == expected
@failsonjython
def test_idem_compile_and_getsource(): def test_idem_compile_and_getsource():
from py._code.source import getsource from py._code.source import getsource
expected = "def x(): pass" expected = "def x(): pass"
@ -355,8 +349,7 @@ def test_findsource_fallback():
assert 'test_findsource_simple' in str(src) assert 'test_findsource_simple' in str(src)
assert src[lineno] == ' def x():' assert src[lineno] == ' def x():'
@failsonjython def test_findsource():
def test_findsource___source__():
from py._code.source import findsource from py._code.source import findsource
co = py.code.compile("""if 1: co = py.code.compile("""if 1:
def x(): def x():
@ -373,7 +366,6 @@ def test_findsource___source__():
assert src[lineno] == " def x():" assert src[lineno] == " def x():"
@failsonjython
def test_getfslineno(): def test_getfslineno():
from py.code import getfslineno from py.code import getfslineno

View File

@ -316,7 +316,11 @@ def test_runtest_in_module_ordering(testdir):
class TestRaises: class TestRaises:
def test_raises(self): def test_raises(self):
py.test.raises(ValueError, "int('qwe')") source = "int('qwe')"
excinfo = py.test.raises(ValueError, source)
code = excinfo.traceback[-1].frame.code
s = str(code.fullsource)
assert s == source
def test_raises_exec(self): def test_raises_exec(self):
py.test.raises(ValueError, "a,x = []") py.test.raises(ValueError, "a,x = []")