From commits-noreply at bitbucket.org Wed Jun 1 08:00:26 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 01 Jun 2011 06:00:26 -0000 Subject: [py-svn] commit/pytest: hpk42: update authors to reflect more current situation Message-ID: <20110601060026.21645.91676@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/93c877e3a6ea/ changeset: 93c877e3a6ea branches: user: hpk42 date: 2011-06-01 08:00:12 summary: update authors to reflect more current situation affected #: 2 files (183 bytes) --- a/AUTHORS Tue May 31 15:21:08 2011 +0200 +++ b/AUTHORS Wed Jun 01 08:00:12 2011 +0200 @@ -1,16 +1,17 @@ Holger Krekel, holger at merlinux eu -Benjamin Peterson, benjamin at python org -Ronny Pfannschmidt, Ronny.Pfannschmidt at gmx de -Guido Wesdorp, johnny at johnnydebris net -Samuele Pedroni, pedronis at openend se -Carl Friedrich Bolz, cfbolz at gmx de -Armin Rigo, arigo at tunes org -Maciek Fijalkowski, fijal at genesilico pl -Brian Dorsey, briandorsey at gmail com merlinux GmbH, Germany, office at merlinux eu Contributors include:: +Ronny Pfannschmidt +Benjamin Peterson +Floris Bruynooghe +Samuele Pedroni +Carl Friedrich Bolz +Armin Rigo +Maciek Fijalkowski +Guido Wesdorp +Brian Dorsey Ross Lawley Ralf Schmitt Chris Lamb --- a/setup.py Tue May 31 15:21:08 2011 +0200 +++ b/setup.py Wed Jun 01 08:00:12 2011 +0200 @@ -9,13 +9,13 @@ Platforms: Linux, Win32, OSX -Interpreters: Python versions 2.4 through to 3.2, Jython 2.5.1 and PyPy +Interpreters: Python versions 2.4 through to 3.2, Jython 2.5.1 and PyPy-1.5 Bugs and issues: http://bitbucket.org/hpk42/pytest/issues/ Web page: http://pytest.org -(c) Holger Krekel and others, 2004-2010 +(c) Holger Krekel and others, 2004-2011 """ def main(): setup( @@ -26,7 +26,7 @@ url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], - author='holger krekel, Guido Wesdorp, Carl Friedrich Bolz, Armin Rigo, Maciej Fijalkowski & others', + author='Holger Krekel, Benjamin Peterson, Ronny Pfannschmidt, Floris Bruynooghe and others', author_email='holger at merlinux.eu', entry_points= make_entry_points(), install_requires=['py>1.4.3'], @@ -67,4 +67,4 @@ return {'console_scripts': l} if __name__ == '__main__': - main() \ No newline at end of file + main() Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Jun 1 08:03:14 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 01 Jun 2011 06:03:14 -0000 Subject: [py-svn] commit/pytest: hpk42: fix issue48 - test and fix typo in MarkInfo repr Message-ID: <20110601060314.10785.80017@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/6e3f8d84dfbf/ changeset: 6e3f8d84dfbf branches: user: hpk42 date: 2011-06-01 08:03:06 summary: fix issue48 - test and fix typo in MarkInfo repr affected #: 3 files (193 bytes) --- a/CHANGELOG Wed Jun 01 08:00:12 2011 +0200 +++ b/CHANGELOG Wed Jun 01 08:03:06 2011 +0200 @@ -8,6 +8,7 @@ - fix issue43: improve doctests with better traceback reporting on unexpected exceptions - fix issue47: timing output in junitxml for test cases is now correct +- fix issue48: typo in MarkInfo repr leading to exception - introduce XXX pytest_configure_funcargs hack (thanks Ronny) - env/username expansion for junitxml file path (fixes issue44) --- a/_pytest/mark.py Wed Jun 01 08:00:12 2011 +0200 +++ b/_pytest/mark.py Wed Jun 01 08:03:06 2011 +0200 @@ -153,7 +153,7 @@ def __repr__(self): return "" % ( - self._name, self.args, self.kwargs) + self.name, self.args, self.kwargs) def pytest_itemcollected(item): if not isinstance(item, pytest.Function): --- a/testing/test_mark.py Wed Jun 01 08:00:12 2011 +0200 +++ b/testing/test_mark.py Wed Jun 01 08:03:06 2011 +0200 @@ -2,6 +2,11 @@ from _pytest.mark import MarkGenerator as Mark class TestMark: + def test_markinfo_repr(self): + from _pytest.mark import MarkInfo + m = MarkInfo("hello", (1,2), {}) + repr(m) + def test_pytest_exists_in_namespace_all(self): assert 'mark' in py.test.__all__ assert 'mark' in pytest.__all__ Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Jun 1 08:46:34 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 01 Jun 2011 06:46:34 -0000 Subject: [py-svn] commit/apipkg: hpk42: add LICENSE file Message-ID: <20110601064634.10786.98246@bitbucket01.managed.contegix.com> 1 new changeset in apipkg: http://bitbucket.org/hpk42/apipkg/changeset/bd637d83de11/ changeset: bd637d83de11 branches: user: hpk42 date: 2011-06-01 08:46:22 summary: add LICENSE file affected #: 1 file (1.0 KB) Repository URL: https://bitbucket.org/hpk42/apipkg/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Jun 1 08:47:22 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 01 Jun 2011 06:47:22 -0000 Subject: [py-svn] commit/apipkg: hpk42: bump version Message-ID: <20110601064722.10786.68020@bitbucket01.managed.contegix.com> 1 new changeset in apipkg: http://bitbucket.org/hpk42/apipkg/changeset/7ecd687d0ae4/ changeset: 7ecd687d0ae4 branches: user: hpk42 date: 2011-06-01 08:47:14 summary: bump version affected #: 2 files (1 byte) --- a/apipkg.py Wed Jun 01 08:46:22 2011 +0200 +++ b/apipkg.py Wed Jun 01 08:47:14 2011 +0200 @@ -9,7 +9,7 @@ import sys from types import ModuleType -__version__ = '1.2.dev6' +__version__ = '1.2.dev7' def initpkg(pkgname, exportdefs, attr=dict()): """ initialize given package from the export definitions. """ --- a/setup.py Wed Jun 01 08:46:22 2011 +0200 +++ b/setup.py Wed Jun 01 08:47:14 2011 +0200 @@ -19,7 +19,7 @@ description= 'apipkg: namespace control and lazy-import mechanism', long_description = open('README.txt').read(), - version='1.2.dev6', + version='1.2.dev7', url='http://bitbucket.org/hpk42/apipkg', license='MIT License', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -38,4 +38,4 @@ ) if __name__ == '__main__': - main() \ No newline at end of file + main() Repository URL: https://bitbucket.org/hpk42/apipkg/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Jun 1 14:55:17 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 01 Jun 2011 12:55:17 -0000 Subject: [py-svn] commit/pytest: hpk42: fix issue49 - avoid confusing errors when initialization goes wrong Message-ID: <20110601125517.18346.69009@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/502331edf550/ changeset: 502331edf550 branches: user: hpk42 date: 2011-06-01 14:54:34 summary: fix issue49 - avoid confusing errors when initialization goes wrong affected #: 2 files (543 bytes) --- a/_pytest/main.py Wed Jun 01 08:03:06 2011 +0200 +++ b/_pytest/main.py Wed Jun 01 14:54:34 2011 +0200 @@ -58,9 +58,12 @@ """Skeleton command line program""" session = Session(config) session.exitstatus = EXIT_OK + initstate = 0 try: config.pluginmanager.do_configure(config) + initstate = 1 config.hook.pytest_sessionstart(session=session) + initstate = 2 doit(config, session) except pytest.UsageError: raise @@ -76,9 +79,11 @@ sys.stderr.write("mainloop: caught Spurious SystemExit!\n") if not session.exitstatus and session._testsfailed: session.exitstatus = EXIT_TESTSFAILED - config.hook.pytest_sessionfinish(session=session, - exitstatus=session.exitstatus) - config.pluginmanager.do_unconfigure(config) + if initstate >= 2: + config.hook.pytest_sessionfinish(session=session, + exitstatus=session.exitstatus) + if initstate >= 1: + config.pluginmanager.do_unconfigure(config) return session.exitstatus def pytest_cmdline_main(config): --- a/testing/acceptance_test.py Wed Jun 01 08:03:06 2011 +0200 +++ b/testing/acceptance_test.py Wed Jun 01 14:54:34 2011 +0200 @@ -259,6 +259,19 @@ if name.startswith("pytest_"): assert value.__doc__, "no docstring for %s" % name + def test_initialization_error_issue49(self, testdir): + testdir.makeconftest(""" + def pytest_configure(): + x + """) + result = testdir.runpytest() + assert result.ret == 3 # internal error + result.stderr.fnmatch_lines([ + "INTERNAL*pytest_configure*", + "INTERNAL*x*", + ]) + assert 'sessionstarttime' not in result.stderr.str() + class TestInvocationVariants: def test_earlyinit(self, testdir): p = testdir.makepyfile(""" Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Jun 1 14:55:57 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 01 Jun 2011 12:55:57 -0000 Subject: [py-svn] commit/pytest: hpk42: add issue49 fix to CHANGELOG Message-ID: <20110601125557.18349.47236@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/ebde6b69c2ef/ changeset: ebde6b69c2ef branches: user: hpk42 date: 2011-06-01 14:55:50 summary: add issue49 fix to CHANGELOG affected #: 1 file (73 bytes) --- a/CHANGELOG Wed Jun 01 14:54:34 2011 +0200 +++ b/CHANGELOG Wed Jun 01 14:55:50 2011 +0200 @@ -9,6 +9,7 @@ unexpected exceptions - fix issue47: timing output in junitxml for test cases is now correct - fix issue48: typo in MarkInfo repr leading to exception +- fix issue49: avoid confusing error when initizaliation partially fails - introduce XXX pytest_configure_funcargs hack (thanks Ronny) - env/username expansion for junitxml file path (fixes issue44) Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Jun 1 15:10:18 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 01 Jun 2011 13:10:18 -0000 Subject: [py-svn] commit/pytest: hpk42: back out pytest_configure_funcargs hook for now Message-ID: <20110601131018.10784.22495@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/324651173fed/ changeset: 324651173fed branches: user: hpk42 date: 2011-06-01 15:08:54 summary: back out pytest_configure_funcargs hook for now affected #: 7 files (1.2 KB) --- a/CHANGELOG Wed Jun 01 14:55:50 2011 +0200 +++ b/CHANGELOG Wed Jun 01 15:08:54 2011 +0200 @@ -10,8 +10,7 @@ - fix issue47: timing output in junitxml for test cases is now correct - fix issue48: typo in MarkInfo repr leading to exception - fix issue49: avoid confusing error when initizaliation partially fails -- introduce XXX pytest_configure_funcargs hack (thanks Ronny) -- env/username expansion for junitxml file path (fixes issue44) +- fix issue44: env/username expansion for junitxml file path Changes between 2.0.2 and 2.0.3 ---------------------------------------------- --- a/_pytest/__init__.py Wed Jun 01 14:55:50 2011 +0200 +++ b/_pytest/__init__.py Wed Jun 01 15:08:54 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.0.dev3' +__version__ = '2.1.0.dev4' --- a/_pytest/hookspec.py Wed Jun 01 14:55:50 2011 +0200 +++ b/_pytest/hookspec.py Wed Jun 01 15:08:54 2011 +0200 @@ -115,9 +115,6 @@ def pytest_generate_tests(metafunc): """ generate (multiple) parametrized calls to a test function.""" -def pytest_configure_funcargs(request): - """ configure funcargs """ - # ------------------------------------------------------------------------- # generic runtest related hooks # ------------------------------------------------------------------------- --- a/_pytest/python.py Wed Jun 01 14:55:50 2011 +0200 +++ b/_pytest/python.py Wed Jun 01 15:08:54 2011 +0200 @@ -505,16 +505,6 @@ request = FuncargRequest(pyfuncitem=function) request._fillfuncargs() -def pytest_configure_funcargs(request): - argnames = getfuncargnames(request.function) - if argnames: - item = request._pyfuncitem - assert not getattr(item, '_args', None), ( - "yielded functions cannot have funcargs") - for argname in argnames: - if argname not in item.funcargs: - item.funcargs[argname] = request.getfuncargvalue(argname) - _notexists = object() class CallSpec: def __init__(self, funcargs, id, param): @@ -636,6 +626,14 @@ """ the file system path of the test module which collected this test. """ return self._pyfuncitem.fspath + def _fillfuncargs(self): + argnames = getfuncargnames(self.function) + if argnames: + assert not getattr(self._pyfuncitem, '_args', None), ( + "yielded functions cannot have funcargs") + for argname in argnames: + if argname not in self._pyfuncitem.funcargs: + self._pyfuncitem.funcargs[argname] = self.getfuncargvalue(argname) def applymarker(self, marker): @@ -707,9 +705,6 @@ self._currentarg = oldarg return res - def _fillfuncargs(self): - self.config.hook.pytest_configure_funcargs.pcall(self._plugins, request=self) - def _getscopeitem(self, scope): if scope == "function": return self._pyfuncitem --- a/doc/funcargs.txt Wed Jun 01 14:55:50 2011 +0200 +++ b/doc/funcargs.txt Wed Jun 01 15:08:54 2011 +0200 @@ -115,9 +115,6 @@ .. _`funcarg factory`: .. _factory: - - - The funcarg **request** object ============================================= @@ -143,16 +140,6 @@ .. _`parametrizing-tests`: .. _`parametrized test functions`: - -Reconfiguring funcargs in a test's setup -======================================== - -Sometimes there is need to do additional funcarg setup steps -which are outside of the normal setup and involve more than just one funcarg. -For that reason the ``pytest_configure_funcargs(request)`` hook -is called to implement and extend the funcarg filling mechanism. - - Parametrizing multiple calls to a test function =========================================================== --- a/setup.py Wed Jun 01 14:55:50 2011 +0200 +++ b/setup.py Wed Jun 01 15:08:54 2011 +0200 @@ -22,7 +22,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.0.dev3', + version='2.1.0.dev4', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -67,4 +67,4 @@ return {'console_scripts': l} if __name__ == '__main__': - main() + main() \ No newline at end of file --- a/testing/test_python.py Wed Jun 01 14:55:50 2011 +0200 +++ b/testing/test_python.py Wed Jun 01 15:08:54 2011 +0200 @@ -606,22 +606,6 @@ fillfuncargs(item) assert len(item.funcargs) == 1 - def test_configure_hook(self, testdir): - item = testdir.getitem("def test_func(some, other=20): pass") - class Provider: - def pytest_funcarg__some(self, request): - return [] - def pytest_configure_funcargs(self, request): - request.getfuncargvalue('some').append(1) - item.config.pluginmanager.register(Provider()) - if hasattr(item, '_args'): - del item._args - from _pytest.python import fillfuncargs - fillfuncargs(item) - assert len(item.funcargs) == 1 - assert item.funcargs['some'] == [1] - - class TestRequest: def test_request_attributes(self, testdir): item = testdir.getitem(""" Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Fri Jun 3 20:49:38 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 03 Jun 2011 18:49:38 -0000 Subject: [py-svn] commit/py: 3 new changesets Message-ID: <20110603184938.10785.84475@bitbucket01.managed.contegix.com> 3 new changesets in py: http://bitbucket.org/hpk42/py/changeset/306515524df8/ changeset: 306515524df8 branches: user: gutworth date: 2011-06-03 20:44:01 summary: remove duplicate test affected #: 1 file (590 bytes) --- a/testing/io_/test_saferepr.py Wed May 25 17:44:46 2011 -0500 +++ b/testing/io_/test_saferepr.py Fri Jun 03 13:44:01 2011 -0500 @@ -56,23 +56,6 @@ except Exception: py.test.fail("saferepr failed for newstyle class") -def test_builtin_patch_unpatch(monkeypatch): - cpy_builtin = py.builtin.builtins - comp = cpy_builtin.compile - def mycompile(*args, **kwargs): - return comp(*args, **kwargs) - class Sub(AssertionError): - pass - monkeypatch.setattr(cpy_builtin, 'AssertionError', Sub) - monkeypatch.setattr(cpy_builtin, 'compile', mycompile) - py.code.patch_builtins() - assert cpy_builtin.AssertionError != Sub - assert cpy_builtin.compile != mycompile - py.code.unpatch_builtins() - assert cpy_builtin.AssertionError is Sub - assert cpy_builtin.compile == mycompile - - def test_unicode_handling(): value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8') def f(): http://bitbucket.org/hpk42/py/changeset/e2bc0b67063e/ changeset: e2bc0b67063e branches: user: gutworth date: 2011-06-03 20:49:13 summary: remove all assertion introspection code affected #: 9 files (2.6 KB) --- a/CHANGELOG Fri Jun 03 13:44:01 2011 -0500 +++ b/CHANGELOG Fri Jun 03 13:49:13 2011 -0500 @@ -1,3 +1,10 @@ +Major changes +============= + +- Remove all code related to assertion introspection. This affects several + public APIs: py.code.patch_builtins/unpatch_builtin lost the assertion + argument. Traceback entries no longer have a reinterpret method. + Changes between 1.4.2 and 1.4.3 ================================================== --- a/py/__init__.py Fri Jun 03 13:44:01 2011 -0500 +++ b/py/__init__.py Fri Jun 03 13:49:13 2011 -0500 @@ -70,11 +70,6 @@ 'getrawcode' : '._code.code:getrawcode', 'patch_builtins' : '._code.code:patch_builtins', 'unpatch_builtins' : '._code.code:unpatch_builtins', - '_AssertionError' : '._code.assertion:AssertionError', - '_reinterpret_old' : '._code.assertion:reinterpret_old', - '_reinterpret' : '._code.assertion:reinterpret', - '_reprcompare' : '._code.assertion:_reprcompare', - '_format_explanation' : '._code.assertion:_format_explanation', }, # backports and additions of builtins --- a/py/_code/_assertionnew.py Fri Jun 03 13:44:01 2011 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,339 +0,0 @@ -""" -Find intermediate evalutation results in assert statements through builtin AST. -This should replace _assertionold.py eventually. -""" - -import sys -import ast - -import py -from py._code.assertion import _format_explanation, BuiltinAssertionError - - -if sys.platform.startswith("java") and sys.version_info < (2, 5, 2): - # See http://bugs.jython.org/issue1497 - _exprs = ("BoolOp", "BinOp", "UnaryOp", "Lambda", "IfExp", "Dict", - "ListComp", "GeneratorExp", "Yield", "Compare", "Call", - "Repr", "Num", "Str", "Attribute", "Subscript", "Name", - "List", "Tuple") - _stmts = ("FunctionDef", "ClassDef", "Return", "Delete", "Assign", - "AugAssign", "Print", "For", "While", "If", "With", "Raise", - "TryExcept", "TryFinally", "Assert", "Import", "ImportFrom", - "Exec", "Global", "Expr", "Pass", "Break", "Continue") - _expr_nodes = set(getattr(ast, name) for name in _exprs) - _stmt_nodes = set(getattr(ast, name) for name in _stmts) - def _is_ast_expr(node): - return node.__class__ in _expr_nodes - def _is_ast_stmt(node): - return node.__class__ in _stmt_nodes -else: - def _is_ast_expr(node): - return isinstance(node, ast.expr) - def _is_ast_stmt(node): - return isinstance(node, ast.stmt) - - -class Failure(Exception): - """Error found while interpreting AST.""" - - def __init__(self, explanation=""): - self.cause = sys.exc_info() - self.explanation = explanation - - -def interpret(source, frame, should_fail=False): - mod = ast.parse(source) - visitor = DebugInterpreter(frame) - try: - visitor.visit(mod) - except Failure: - failure = sys.exc_info()[1] - return getfailure(failure) - if should_fail: - return ("(assertion failed, but when it was re-run for " - "printing intermediate values, it did not fail. Suggestions: " - "compute assert expression before the assert or use --no-assert)") - -def run(offending_line, frame=None): - if frame is None: - frame = py.code.Frame(sys._getframe(1)) - return interpret(offending_line, frame) - -def getfailure(failure): - explanation = _format_explanation(failure.explanation) - value = failure.cause[1] - if str(value): - lines = explanation.splitlines() - if not lines: - lines.append("") - lines[0] += " << %s" % (value,) - explanation = "\n".join(lines) - text = "%s: %s" % (failure.cause[0].__name__, explanation) - if text.startswith("AssertionError: assert "): - text = text[16:] - return text - - -operator_map = { - ast.BitOr : "|", - ast.BitXor : "^", - ast.BitAnd : "&", - ast.LShift : "<<", - ast.RShift : ">>", - ast.Add : "+", - ast.Sub : "-", - ast.Mult : "*", - ast.Div : "/", - ast.FloorDiv : "//", - ast.Mod : "%", - ast.Eq : "==", - ast.NotEq : "!=", - ast.Lt : "<", - ast.LtE : "<=", - ast.Gt : ">", - ast.GtE : ">=", - ast.Pow : "**", - ast.Is : "is", - ast.IsNot : "is not", - ast.In : "in", - ast.NotIn : "not in" -} - -unary_map = { - ast.Not : "not %s", - ast.Invert : "~%s", - ast.USub : "-%s", - ast.UAdd : "+%s" -} - - -class DebugInterpreter(ast.NodeVisitor): - """Interpret AST nodes to gleam useful debugging information. """ - - def __init__(self, frame): - self.frame = frame - - def generic_visit(self, node): - # Fallback when we don't have a special implementation. - if _is_ast_expr(node): - mod = ast.Expression(node) - co = self._compile(mod) - try: - result = self.frame.eval(co) - except Exception: - raise Failure() - explanation = self.frame.repr(result) - return explanation, result - elif _is_ast_stmt(node): - mod = ast.Module([node]) - co = self._compile(mod, "exec") - try: - self.frame.exec_(co) - except Exception: - raise Failure() - return None, None - else: - raise AssertionError("can't handle %s" %(node,)) - - def _compile(self, source, mode="eval"): - return compile(source, "", mode) - - def visit_Expr(self, expr): - return self.visit(expr.value) - - def visit_Module(self, mod): - for stmt in mod.body: - self.visit(stmt) - - def visit_Name(self, name): - explanation, result = self.generic_visit(name) - # See if the name is local. - source = "%r in locals() is not globals()" % (name.id,) - co = self._compile(source) - try: - local = self.frame.eval(co) - except Exception: - # have to assume it isn't - local = False - if not local: - return name.id, result - return explanation, result - - def visit_Compare(self, comp): - left = comp.left - left_explanation, left_result = self.visit(left) - for op, next_op in zip(comp.ops, comp.comparators): - next_explanation, next_result = self.visit(next_op) - op_symbol = operator_map[op.__class__] - explanation = "%s %s %s" % (left_explanation, op_symbol, - next_explanation) - source = "__exprinfo_left %s __exprinfo_right" % (op_symbol,) - co = self._compile(source) - try: - result = self.frame.eval(co, __exprinfo_left=left_result, - __exprinfo_right=next_result) - except Exception: - raise Failure(explanation) - try: - if not result: - break - except KeyboardInterrupt: - raise - except: - break - left_explanation, left_result = next_explanation, next_result - - rcomp = py.code._reprcompare - if rcomp: - res = rcomp(op_symbol, left_result, next_result) - if res: - explanation = res - return explanation, result - - def visit_BoolOp(self, boolop): - is_or = isinstance(boolop.op, ast.Or) - explanations = [] - for operand in boolop.values: - explanation, result = self.visit(operand) - explanations.append(explanation) - if result == is_or: - break - name = is_or and " or " or " and " - explanation = "(" + name.join(explanations) + ")" - return explanation, result - - def visit_UnaryOp(self, unary): - pattern = unary_map[unary.op.__class__] - operand_explanation, operand_result = self.visit(unary.operand) - explanation = pattern % (operand_explanation,) - co = self._compile(pattern % ("__exprinfo_expr",)) - try: - result = self.frame.eval(co, __exprinfo_expr=operand_result) - except Exception: - raise Failure(explanation) - return explanation, result - - def visit_BinOp(self, binop): - left_explanation, left_result = self.visit(binop.left) - right_explanation, right_result = self.visit(binop.right) - symbol = operator_map[binop.op.__class__] - explanation = "(%s %s %s)" % (left_explanation, symbol, - right_explanation) - source = "__exprinfo_left %s __exprinfo_right" % (symbol,) - co = self._compile(source) - try: - result = self.frame.eval(co, __exprinfo_left=left_result, - __exprinfo_right=right_result) - except Exception: - raise Failure(explanation) - return explanation, result - - def visit_Call(self, call): - func_explanation, func = self.visit(call.func) - arg_explanations = [] - ns = {"__exprinfo_func" : func} - arguments = [] - for arg in call.args: - arg_explanation, arg_result = self.visit(arg) - arg_name = "__exprinfo_%s" % (len(ns),) - ns[arg_name] = arg_result - arguments.append(arg_name) - arg_explanations.append(arg_explanation) - for keyword in call.keywords: - arg_explanation, arg_result = self.visit(keyword.value) - arg_name = "__exprinfo_%s" % (len(ns),) - ns[arg_name] = arg_result - keyword_source = "%s=%%s" % (keyword.arg) - arguments.append(keyword_source % (arg_name,)) - arg_explanations.append(keyword_source % (arg_explanation,)) - if call.starargs: - arg_explanation, arg_result = self.visit(call.starargs) - arg_name = "__exprinfo_star" - ns[arg_name] = arg_result - arguments.append("*%s" % (arg_name,)) - arg_explanations.append("*%s" % (arg_explanation,)) - if call.kwargs: - arg_explanation, arg_result = self.visit(call.kwargs) - arg_name = "__exprinfo_kwds" - ns[arg_name] = arg_result - arguments.append("**%s" % (arg_name,)) - arg_explanations.append("**%s" % (arg_explanation,)) - args_explained = ", ".join(arg_explanations) - explanation = "%s(%s)" % (func_explanation, args_explained) - args = ", ".join(arguments) - source = "__exprinfo_func(%s)" % (args,) - co = self._compile(source) - try: - result = self.frame.eval(co, **ns) - except Exception: - raise Failure(explanation) - pattern = "%s\n{%s = %s\n}" - rep = self.frame.repr(result) - explanation = pattern % (rep, rep, explanation) - return explanation, result - - def _is_builtin_name(self, name): - pattern = "%r not in globals() and %r not in locals()" - source = pattern % (name.id, name.id) - co = self._compile(source) - try: - return self.frame.eval(co) - except Exception: - return False - - def visit_Attribute(self, attr): - if not isinstance(attr.ctx, ast.Load): - return self.generic_visit(attr) - source_explanation, source_result = self.visit(attr.value) - explanation = "%s.%s" % (source_explanation, attr.attr) - source = "__exprinfo_expr.%s" % (attr.attr,) - co = self._compile(source) - try: - result = self.frame.eval(co, __exprinfo_expr=source_result) - except Exception: - raise Failure(explanation) - explanation = "%s\n{%s = %s.%s\n}" % (self.frame.repr(result), - self.frame.repr(result), - source_explanation, attr.attr) - # Check if the attr is from an instance. - source = "%r in getattr(__exprinfo_expr, '__dict__', {})" - source = source % (attr.attr,) - co = self._compile(source) - try: - from_instance = self.frame.eval(co, __exprinfo_expr=source_result) - except Exception: - from_instance = True - if from_instance: - rep = self.frame.repr(result) - pattern = "%s\n{%s = %s\n}" - explanation = pattern % (rep, rep, explanation) - return explanation, result - - def visit_Assert(self, assrt): - test_explanation, test_result = self.visit(assrt.test) - if test_explanation.startswith("False\n{False =") and \ - test_explanation.endswith("\n"): - test_explanation = test_explanation[15:-2] - explanation = "assert %s" % (test_explanation,) - if not test_result: - try: - raise BuiltinAssertionError - except Exception: - raise Failure(explanation) - return explanation, test_result - - def visit_Assign(self, assign): - value_explanation, value_result = self.visit(assign.value) - explanation = "... = %s" % (value_explanation,) - name = ast.Name("__exprinfo_expr", ast.Load(), - lineno=assign.value.lineno, - col_offset=assign.value.col_offset) - new_assign = ast.Assign(assign.targets, name, lineno=assign.lineno, - col_offset=assign.col_offset) - mod = ast.Module([new_assign]) - co = self._compile(mod, "exec") - try: - self.frame.exec_(co, __exprinfo_expr=value_result) - except Exception: - raise Failure(explanation) - return explanation, value_result --- a/py/_code/_assertionold.py Fri Jun 03 13:44:01 2011 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,555 +0,0 @@ -import py -import sys, inspect -from compiler import parse, ast, pycodegen -from py._code.assertion import BuiltinAssertionError, _format_explanation - -passthroughex = py.builtin._sysex - -class Failure: - def __init__(self, node): - self.exc, self.value, self.tb = sys.exc_info() - self.node = node - -class View(object): - """View base class. - - If C is a subclass of View, then C(x) creates a proxy object around - the object x. The actual class of the proxy is not C in general, - but a *subclass* of C determined by the rules below. To avoid confusion - we call view class the class of the proxy (a subclass of C, so of View) - and object class the class of x. - - Attributes and methods not found in the proxy are automatically read on x. - Other operations like setting attributes are performed on the proxy, as - determined by its view class. The object x is available from the proxy - as its __obj__ attribute. - - The view class selection is determined by the __view__ tuples and the - optional __viewkey__ method. By default, the selected view class is the - most specific subclass of C whose __view__ mentions the class of x. - If no such subclass is found, the search proceeds with the parent - object classes. For example, C(True) will first look for a subclass - of C with __view__ = (..., bool, ...) and only if it doesn't find any - look for one with __view__ = (..., int, ...), and then ..., object,... - If everything fails the class C itself is considered to be the default. - - Alternatively, the view class selection can be driven by another aspect - of the object x, instead of the class of x, by overriding __viewkey__. - See last example at the end of this module. - """ - - _viewcache = {} - __view__ = () - - def __new__(rootclass, obj, *args, **kwds): - self = object.__new__(rootclass) - self.__obj__ = obj - self.__rootclass__ = rootclass - key = self.__viewkey__() - try: - self.__class__ = self._viewcache[key] - except KeyError: - self.__class__ = self._selectsubclass(key) - return self - - def __getattr__(self, attr): - # attributes not found in the normal hierarchy rooted on View - # are looked up in the object's real class - return getattr(self.__obj__, attr) - - def __viewkey__(self): - return self.__obj__.__class__ - - def __matchkey__(self, key, subclasses): - if inspect.isclass(key): - keys = inspect.getmro(key) - else: - keys = [key] - for key in keys: - result = [C for C in subclasses if key in C.__view__] - if result: - return result - return [] - - def _selectsubclass(self, key): - subclasses = list(enumsubclasses(self.__rootclass__)) - for C in subclasses: - if not isinstance(C.__view__, tuple): - C.__view__ = (C.__view__,) - choices = self.__matchkey__(key, subclasses) - if not choices: - return self.__rootclass__ - elif len(choices) == 1: - return choices[0] - else: - # combine the multiple choices - return type('?', tuple(choices), {}) - - def __repr__(self): - return '%s(%r)' % (self.__rootclass__.__name__, self.__obj__) - - -def enumsubclasses(cls): - for subcls in cls.__subclasses__(): - for subsubclass in enumsubclasses(subcls): - yield subsubclass - yield cls - - -class Interpretable(View): - """A parse tree node with a few extra methods.""" - explanation = None - - def is_builtin(self, frame): - return False - - def eval(self, frame): - # fall-back for unknown expression nodes - try: - expr = ast.Expression(self.__obj__) - expr.filename = '' - self.__obj__.filename = '' - co = pycodegen.ExpressionCodeGenerator(expr).getCode() - result = frame.eval(co) - except passthroughex: - raise - except: - raise Failure(self) - self.result = result - self.explanation = self.explanation or frame.repr(self.result) - - def run(self, frame): - # fall-back for unknown statement nodes - try: - expr = ast.Module(None, ast.Stmt([self.__obj__])) - expr.filename = '' - co = pycodegen.ModuleCodeGenerator(expr).getCode() - frame.exec_(co) - except passthroughex: - raise - except: - raise Failure(self) - - def nice_explanation(self): - return _format_explanation(self.explanation) - - -class Name(Interpretable): - __view__ = ast.Name - - def is_local(self, frame): - source = '%r in locals() is not globals()' % self.name - try: - return frame.is_true(frame.eval(source)) - except passthroughex: - raise - except: - return False - - def is_global(self, frame): - source = '%r in globals()' % self.name - try: - return frame.is_true(frame.eval(source)) - except passthroughex: - raise - except: - return False - - def is_builtin(self, frame): - source = '%r not in locals() and %r not in globals()' % ( - self.name, self.name) - try: - return frame.is_true(frame.eval(source)) - except passthroughex: - raise - except: - return False - - def eval(self, frame): - super(Name, self).eval(frame) - if not self.is_local(frame): - self.explanation = self.name - -class Compare(Interpretable): - __view__ = ast.Compare - - def eval(self, frame): - expr = Interpretable(self.expr) - expr.eval(frame) - for operation, expr2 in self.ops: - if hasattr(self, 'result'): - # shortcutting in chained expressions - if not frame.is_true(self.result): - break - expr2 = Interpretable(expr2) - expr2.eval(frame) - self.explanation = "%s %s %s" % ( - expr.explanation, operation, expr2.explanation) - source = "__exprinfo_left %s __exprinfo_right" % operation - try: - self.result = frame.eval(source, - __exprinfo_left=expr.result, - __exprinfo_right=expr2.result) - except passthroughex: - raise - except: - raise Failure(self) - expr = expr2 - -class And(Interpretable): - __view__ = ast.And - - def eval(self, frame): - explanations = [] - for expr in self.nodes: - expr = Interpretable(expr) - expr.eval(frame) - explanations.append(expr.explanation) - self.result = expr.result - if not frame.is_true(expr.result): - break - self.explanation = '(' + ' and '.join(explanations) + ')' - -class Or(Interpretable): - __view__ = ast.Or - - def eval(self, frame): - explanations = [] - for expr in self.nodes: - expr = Interpretable(expr) - expr.eval(frame) - explanations.append(expr.explanation) - self.result = expr.result - if frame.is_true(expr.result): - break - self.explanation = '(' + ' or '.join(explanations) + ')' - - -# == Unary operations == -keepalive = [] -for astclass, astpattern in { - ast.Not : 'not __exprinfo_expr', - ast.Invert : '(~__exprinfo_expr)', - }.items(): - - class UnaryArith(Interpretable): - __view__ = astclass - - def eval(self, frame, astpattern=astpattern): - expr = Interpretable(self.expr) - expr.eval(frame) - self.explanation = astpattern.replace('__exprinfo_expr', - expr.explanation) - try: - self.result = frame.eval(astpattern, - __exprinfo_expr=expr.result) - except passthroughex: - raise - except: - raise Failure(self) - - keepalive.append(UnaryArith) - -# == Binary operations == -for astclass, astpattern in { - ast.Add : '(__exprinfo_left + __exprinfo_right)', - ast.Sub : '(__exprinfo_left - __exprinfo_right)', - ast.Mul : '(__exprinfo_left * __exprinfo_right)', - ast.Div : '(__exprinfo_left / __exprinfo_right)', - ast.Mod : '(__exprinfo_left % __exprinfo_right)', - ast.Power : '(__exprinfo_left ** __exprinfo_right)', - }.items(): - - class BinaryArith(Interpretable): - __view__ = astclass - - def eval(self, frame, astpattern=astpattern): - left = Interpretable(self.left) - left.eval(frame) - right = Interpretable(self.right) - right.eval(frame) - self.explanation = (astpattern - .replace('__exprinfo_left', left .explanation) - .replace('__exprinfo_right', right.explanation)) - try: - self.result = frame.eval(astpattern, - __exprinfo_left=left.result, - __exprinfo_right=right.result) - except passthroughex: - raise - except: - raise Failure(self) - - keepalive.append(BinaryArith) - - -class CallFunc(Interpretable): - __view__ = ast.CallFunc - - def is_bool(self, frame): - source = 'isinstance(__exprinfo_value, bool)' - try: - return frame.is_true(frame.eval(source, - __exprinfo_value=self.result)) - except passthroughex: - raise - except: - return False - - def eval(self, frame): - node = Interpretable(self.node) - node.eval(frame) - explanations = [] - vars = {'__exprinfo_fn': node.result} - source = '__exprinfo_fn(' - for a in self.args: - if isinstance(a, ast.Keyword): - keyword = a.name - a = a.expr - else: - keyword = None - a = Interpretable(a) - a.eval(frame) - argname = '__exprinfo_%d' % len(vars) - vars[argname] = a.result - if keyword is None: - source += argname + ',' - explanations.append(a.explanation) - else: - source += '%s=%s,' % (keyword, argname) - explanations.append('%s=%s' % (keyword, a.explanation)) - if self.star_args: - star_args = Interpretable(self.star_args) - star_args.eval(frame) - argname = '__exprinfo_star' - vars[argname] = star_args.result - source += '*' + argname + ',' - explanations.append('*' + star_args.explanation) - if self.dstar_args: - dstar_args = Interpretable(self.dstar_args) - dstar_args.eval(frame) - argname = '__exprinfo_kwds' - vars[argname] = dstar_args.result - source += '**' + argname + ',' - explanations.append('**' + dstar_args.explanation) - self.explanation = "%s(%s)" % ( - node.explanation, ', '.join(explanations)) - if source.endswith(','): - source = source[:-1] - source += ')' - try: - self.result = frame.eval(source, **vars) - except passthroughex: - raise - except: - raise Failure(self) - if not node.is_builtin(frame) or not self.is_bool(frame): - r = frame.repr(self.result) - self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation) - -class Getattr(Interpretable): - __view__ = ast.Getattr - - def eval(self, frame): - expr = Interpretable(self.expr) - expr.eval(frame) - source = '__exprinfo_expr.%s' % self.attrname - try: - self.result = frame.eval(source, __exprinfo_expr=expr.result) - except passthroughex: - raise - except: - raise Failure(self) - self.explanation = '%s.%s' % (expr.explanation, self.attrname) - # if the attribute comes from the instance, its value is interesting - source = ('hasattr(__exprinfo_expr, "__dict__") and ' - '%r in __exprinfo_expr.__dict__' % self.attrname) - try: - from_instance = frame.is_true( - frame.eval(source, __exprinfo_expr=expr.result)) - except passthroughex: - raise - except: - from_instance = True - if from_instance: - r = frame.repr(self.result) - self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation) - -# == Re-interpretation of full statements == - -class Assert(Interpretable): - __view__ = ast.Assert - - def run(self, frame): - test = Interpretable(self.test) - test.eval(frame) - # simplify 'assert False where False = ...' - if (test.explanation.startswith('False\n{False = ') and - test.explanation.endswith('\n}')): - test.explanation = test.explanation[15:-2] - # print the result as 'assert ' - self.result = test.result - self.explanation = 'assert ' + test.explanation - if not frame.is_true(test.result): - try: - raise BuiltinAssertionError - except passthroughex: - raise - except: - raise Failure(self) - -class Assign(Interpretable): - __view__ = ast.Assign - - def run(self, frame): - expr = Interpretable(self.expr) - expr.eval(frame) - self.result = expr.result - self.explanation = '... = ' + expr.explanation - # fall-back-run the rest of the assignment - ass = ast.Assign(self.nodes, ast.Name('__exprinfo_expr')) - mod = ast.Module(None, ast.Stmt([ass])) - mod.filename = '' - co = pycodegen.ModuleCodeGenerator(mod).getCode() - try: - frame.exec_(co, __exprinfo_expr=expr.result) - except passthroughex: - raise - except: - raise Failure(self) - -class Discard(Interpretable): - __view__ = ast.Discard - - def run(self, frame): - expr = Interpretable(self.expr) - expr.eval(frame) - self.result = expr.result - self.explanation = expr.explanation - -class Stmt(Interpretable): - __view__ = ast.Stmt - - def run(self, frame): - for stmt in self.nodes: - stmt = Interpretable(stmt) - stmt.run(frame) - - -def report_failure(e): - explanation = e.node.nice_explanation() - if explanation: - explanation = ", in: " + explanation - else: - explanation = "" - sys.stdout.write("%s: %s%s\n" % (e.exc.__name__, e.value, explanation)) - -def check(s, frame=None): - if frame is None: - frame = sys._getframe(1) - frame = py.code.Frame(frame) - expr = parse(s, 'eval') - assert isinstance(expr, ast.Expression) - node = Interpretable(expr.node) - try: - node.eval(frame) - except passthroughex: - raise - except Failure: - e = sys.exc_info()[1] - report_failure(e) - else: - if not frame.is_true(node.result): - sys.stderr.write("assertion failed: %s\n" % node.nice_explanation()) - - -########################################################### -# API / Entry points -# ######################################################### - -def interpret(source, frame, should_fail=False): - module = Interpretable(parse(source, 'exec').node) - #print "got module", module - if isinstance(frame, py.std.types.FrameType): - frame = py.code.Frame(frame) - try: - module.run(frame) - except Failure: - e = sys.exc_info()[1] - return getfailure(e) - except passthroughex: - raise - except: - import traceback - traceback.print_exc() - if should_fail: - return ("(assertion failed, but when it was re-run for " - "printing intermediate values, it did not fail. Suggestions: " - "compute assert expression before the assert or use --nomagic)") - else: - return None - -def getmsg(excinfo): - if isinstance(excinfo, tuple): - excinfo = py.code.ExceptionInfo(excinfo) - #frame, line = gettbline(tb) - #frame = py.code.Frame(frame) - #return interpret(line, frame) - - tb = excinfo.traceback[-1] - source = str(tb.statement).strip() - x = interpret(source, tb.frame, should_fail=True) - if not isinstance(x, str): - raise TypeError("interpret returned non-string %r" % (x,)) - return x - -def getfailure(e): - explanation = e.node.nice_explanation() - if str(e.value): - lines = explanation.split('\n') - lines[0] += " << %s" % (e.value,) - explanation = '\n'.join(lines) - text = "%s: %s" % (e.exc.__name__, explanation) - if text.startswith('AssertionError: assert '): - text = text[16:] - return text - -def run(s, frame=None): - if frame is None: - frame = sys._getframe(1) - frame = py.code.Frame(frame) - module = Interpretable(parse(s, 'exec').node) - try: - module.run(frame) - except Failure: - e = sys.exc_info()[1] - report_failure(e) - - -if __name__ == '__main__': - # example: - def f(): - return 5 - def g(): - return 3 - def h(x): - return 'never' - check("f() * g() == 5") - check("not f()") - check("not (f() and g() or 0)") - check("f() == g()") - i = 4 - check("i == f()") - check("len(f()) == 0") - check("isinstance(2+3+4, float)") - - run("x = i") - check("x == 5") - - run("assert not f(), 'oops'") - run("a, b, c = 1, 2") - run("a, b, c = f()") - - check("max([f(),g()]) == 4") - check("'hello'[g()] == 'h'") - run("'guk%d' % h(f())") --- a/py/_code/assertion.py Fri Jun 03 13:44:01 2011 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -import sys -import py - -BuiltinAssertionError = py.builtin.builtins.AssertionError - -_reprcompare = None # if set, will be called by assert reinterp for comparison ops - -def _format_explanation(explanation): - """This formats an explanation - - Normally all embedded newlines are escaped, however there are - three exceptions: \n{, \n} and \n~. The first two are intended - cover nested explanations, see function and attribute explanations - for examples (.visit_Call(), visit_Attribute()). The last one is - for when one explanation needs to span multiple lines, e.g. when - displaying diffs. - """ - raw_lines = (explanation or '').split('\n') - # escape newlines not followed by {, } and ~ - lines = [raw_lines[0]] - for l in raw_lines[1:]: - if l.startswith('{') or l.startswith('}') or l.startswith('~'): - lines.append(l) - else: - lines[-1] += '\\n' + l - - result = lines[:1] - stack = [0] - stackcnt = [0] - for line in lines[1:]: - if line.startswith('{'): - if stackcnt[-1]: - s = 'and ' - else: - s = 'where ' - stack.append(len(result)) - stackcnt[-1] += 1 - stackcnt.append(0) - result.append(' +' + ' '*(len(stack)-1) + s + line[1:]) - elif line.startswith('}'): - assert line.startswith('}') - stack.pop() - stackcnt.pop() - result[stack[-1]] += line[1:] - else: - assert line.startswith('~') - result.append(' '*len(stack) + line[1:]) - assert len(stack) == 1 - return '\n'.join(result) - - -class AssertionError(BuiltinAssertionError): - def __init__(self, *args): - BuiltinAssertionError.__init__(self, *args) - if args: - try: - self.msg = str(args[0]) - except py.builtin._sysex: - raise - except: - self.msg = "<[broken __repr__] %s at %0xd>" %( - args[0].__class__, id(args[0])) - else: - f = py.code.Frame(sys._getframe(1)) - try: - source = f.code.fullsource - if source is not None: - try: - source = source.getstatement(f.lineno, assertion=True) - except IndexError: - source = None - else: - source = str(source.deindent()).strip() - except py.error.ENOENT: - source = None - # this can also occur during reinterpretation, when the - # co_filename is set to "". - if source: - self.msg = reinterpret(source, f, should_fail=True) - else: - self.msg = "" - if not self.args: - self.args = (self.msg,) - -if sys.version_info > (3, 0): - AssertionError.__module__ = "builtins" - reinterpret_old = "old reinterpretation not available for py3" -else: - from py._code._assertionold import interpret as reinterpret_old -if sys.version_info >= (2, 6) or (sys.platform.startswith("java")): - from py._code._assertionnew import interpret as reinterpret -else: - reinterpret = reinterpret_old - --- a/py/_code/code.py Fri Jun 03 13:44:01 2011 -0500 +++ b/py/_code/code.py Fri Jun 03 13:49:13 2011 -0500 @@ -145,17 +145,6 @@ return self.frame.f_locals locals = property(getlocals, None, None, "locals of underlaying frame") - def reinterpret(self): - """Reinterpret the failing statement and returns a detailed information - about what operations are performed.""" - if self.exprinfo is None: - source = str(self.statement).strip() - x = py.code._reinterpret(source, self.frame, should_fail=True) - if not isinstance(x, str): - raise TypeError("interpret returned non-string %r" % (x,)) - self.exprinfo = x - return self.exprinfo - def getfirstlinesource(self): # on Jython this firstlineno can be -1 apparently return max(self.frame.code.firstlineno, 0) @@ -690,22 +679,15 @@ oldbuiltins = {} -def patch_builtins(assertion=True, compile=True): - """ put compile and AssertionError builtins to Python's builtins. """ - if assertion: - from py._code import assertion - l = oldbuiltins.setdefault('AssertionError', []) - l.append(py.builtin.builtins.AssertionError) - py.builtin.builtins.AssertionError = assertion.AssertionError +def patch_builtins(compile=True): + """ put compile builtins to Python's builtins. """ if compile: l = oldbuiltins.setdefault('compile', []) l.append(py.builtin.builtins.compile) py.builtin.builtins.compile = py.code.compile -def unpatch_builtins(assertion=True, compile=True): +def unpatch_builtins(compile=True): """ remove compile and AssertionError builtins from Python builtins. """ - if assertion: - py.builtin.builtins.AssertionError = oldbuiltins['AssertionError'].pop() if compile: py.builtin.builtins.compile = oldbuiltins['compile'].pop() --- a/testing/code/test_assertion.py Fri Jun 03 13:44:01 2011 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,331 +0,0 @@ -import pytest, py - -def exvalue(): - return py.std.sys.exc_info()[1] - -def setup_module(mod): - py.code.patch_builtins(assertion=True, compile=False) - -def teardown_module(mod): - py.code.unpatch_builtins(assertion=True, compile=False) - -def f(): - return 2 - -def test_assert(): - try: - assert f() == 3 - except AssertionError: - e = exvalue() - s = str(e) - assert s.startswith('assert 2 == 3\n') - -def test_assert_with_explicit_message(): - try: - assert f() == 3, "hello" - except AssertionError: - e = exvalue() - assert e.msg == 'hello' - -def test_assert_within_finally(): - class A: - def f(): - pass - excinfo = py.test.raises(TypeError, """ - try: - A().f() - finally: - i = 42 - """) - s = excinfo.exconly() - assert s.find("takes no argument") != -1 - - #def g(): - # A.f() - #excinfo = getexcinfo(TypeError, g) - #msg = getmsg(excinfo) - #assert msg.find("must be called with A") != -1 - - -def test_assert_multiline_1(): - try: - assert (f() == - 3) - except AssertionError: - e = exvalue() - s = str(e) - assert s.startswith('assert 2 == 3\n') - -def test_assert_multiline_2(): - try: - assert (f() == (4, - 3)[-1]) - except AssertionError: - e = exvalue() - s = str(e) - assert s.startswith('assert 2 ==') - -def test_in(): - try: - assert "hi" in [1, 2] - except AssertionError: - e = exvalue() - s = str(e) - assert s.startswith("assert 'hi' in") - -def test_is(): - try: - assert 1 is 2 - except AssertionError: - e = exvalue() - s = str(e) - assert s.startswith("assert 1 is 2") - - - at py.test.mark.skipif("sys.version_info < (2,6)") -def test_attrib(): - class Foo(object): - b = 1 - i = Foo() - try: - assert i.b == 2 - except AssertionError: - e = exvalue() - s = str(e) - assert s.startswith("assert 1 == 2") - - at py.test.mark.skipif("sys.version_info < (2,6)") -def test_attrib_inst(): - class Foo(object): - b = 1 - try: - assert Foo().b == 2 - except AssertionError: - e = exvalue() - s = str(e) - assert s.startswith("assert 1 == 2") - -def test_len(): - l = list(range(42)) - try: - assert len(l) == 100 - except AssertionError: - e = exvalue() - s = str(e) - assert s.startswith("assert 42 == 100") - assert "where 42 = len([" in s - -def test_assert_non_string_message(): - class A: - def __str__(self): - return "hello" - try: - assert 0 == 1, A() - except AssertionError: - e = exvalue() - assert e.msg == "hello" - -def test_assert_keyword_arg(): - def f(x=3): - return False - try: - assert f(x=5) - except AssertionError: - e = exvalue() - assert "x=5" in e.msg - -# These tests should both fail, but should fail nicely... -class WeirdRepr: - def __repr__(self): - return '' - -def bug_test_assert_repr(): - v = WeirdRepr() - try: - assert v == 1 - except AssertionError: - e = exvalue() - assert e.msg.find('WeirdRepr') != -1 - assert e.msg.find('second line') != -1 - assert 0 - -def test_assert_non_string(): - try: - assert 0, ['list'] - except AssertionError: - e = exvalue() - assert e.msg.find("list") != -1 - -def test_assert_implicit_multiline(): - try: - x = [1,2,3] - assert x != [1, - 2, 3] - except AssertionError: - e = exvalue() - assert e.msg.find('assert [1, 2, 3] !=') != -1 - - -def test_assert_with_brokenrepr_arg(): - class BrokenRepr: - def __repr__(self): 0 / 0 - e = AssertionError(BrokenRepr()) - if e.msg.find("broken __repr__") == -1: - py.test.fail("broken __repr__ not handle correctly") - -def test_multiple_statements_per_line(): - try: - a = 1; assert a == 2 - except AssertionError: - e = exvalue() - assert "assert 1 == 2" in e.msg - -def test_power(): - try: - assert 2**3 == 7 - except AssertionError: - e = exvalue() - assert "assert (2 ** 3) == 7" in e.msg - - -class TestView: - - def setup_class(cls): - cls.View = py.test.importorskip("py._code._assertionold").View - - def test_class_dispatch(self): - ### Use a custom class hierarchy with existing instances - - class Picklable(self.View): - pass - - class Simple(Picklable): - __view__ = object - def pickle(self): - return repr(self.__obj__) - - class Seq(Picklable): - __view__ = list, tuple, dict - def pickle(self): - return ';'.join( - [Picklable(item).pickle() for item in self.__obj__]) - - class Dict(Seq): - __view__ = dict - def pickle(self): - return Seq.pickle(self) + '!' + Seq(self.values()).pickle() - - assert Picklable(123).pickle() == '123' - assert Picklable([1,[2,3],4]).pickle() == '1;2;3;4' - assert Picklable({1:2}).pickle() == '1!2' - - def test_viewtype_class_hierarchy(self): - # Use a custom class hierarchy based on attributes of existing instances - class Operation: - "Existing class that I don't want to change." - def __init__(self, opname, *args): - self.opname = opname - self.args = args - - existing = [Operation('+', 4, 5), - Operation('getitem', '', 'join'), - Operation('setattr', 'x', 'y', 3), - Operation('-', 12, 1)] - - class PyOp(self.View): - def __viewkey__(self): - return self.opname - def generate(self): - return '%s(%s)' % (self.opname, ', '.join(map(repr, self.args))) - - class PyBinaryOp(PyOp): - __view__ = ('+', '-', '*', '/') - def generate(self): - return '%s %s %s' % (self.args[0], self.opname, self.args[1]) - - codelines = [PyOp(op).generate() for op in existing] - assert codelines == ["4 + 5", "getitem('', 'join')", - "setattr('x', 'y', 3)", "12 - 1"] - -def test_underscore_api(): - py.code._AssertionError - py.code._reinterpret_old # used by pypy - py.code._reinterpret - - at py.test.mark.skipif("sys.version_info < (2,6)") -def test_assert_customizable_reprcompare(monkeypatch): - monkeypatch.setattr(py.code, '_reprcompare', lambda *args: 'hello') - try: - assert 3 == 4 - except AssertionError: - e = exvalue() - s = str(e) - assert "hello" in s - -def test_assert_long_source_1(): - try: - assert len == [ - (None, ['somet text', 'more text']), - ] - except AssertionError: - e = exvalue() - s = str(e) - assert 're-run' not in s - assert 'somet text' in s - -def test_assert_long_source_2(): - try: - assert(len == [ - (None, ['somet text', 'more text']), - ]) - except AssertionError: - e = exvalue() - s = str(e) - assert 're-run' not in s - assert 'somet text' in s - -def test_assert_raise_alias(testdir): - testdir.makepyfile(""" - import sys - EX = AssertionError - def test_hello(): - raise EX("hello" - "multi" - "line") - """) - result = testdir.runpytest() - result.stdout.fnmatch_lines([ - "*def test_hello*", - "*raise EX*", - "*1 failed*", - ]) - - - at pytest.mark.skipif("sys.version_info < (2,5)") -def test_assert_raise_subclass(): - class SomeEx(AssertionError): - def __init__(self, *args): - super(SomeEx, self).__init__() - try: - raise SomeEx("hello") - except AssertionError: - s = str(exvalue()) - assert 're-run' not in s - assert 'could not determine' in s - -def test_assert_raises_in_nonzero_of_object_pytest_issue10(): - class A(object): - def __nonzero__(self): - raise ValueError(42) - def __lt__(self, other): - return A() - def __repr__(self): - return "" - def myany(x): - return True - try: - assert not(myany(A() < 0)) - except AssertionError: - e = exvalue() - s = str(e) - assert "< 0" in s --- a/testing/code/test_code.py Fri Jun 03 13:44:01 2011 -0500 +++ b/testing/code/test_code.py Fri Jun 03 13:49:13 2011 -0500 @@ -68,15 +68,10 @@ comp = cpy_builtin.compile def mycompile(*args, **kwargs): return comp(*args, **kwargs) - class Sub(AssertionError): - pass - monkeypatch.setattr(cpy_builtin, 'AssertionError', Sub) monkeypatch.setattr(cpy_builtin, 'compile', mycompile) py.code.patch_builtins() - assert cpy_builtin.AssertionError != Sub assert cpy_builtin.compile != mycompile py.code.unpatch_builtins() - assert cpy_builtin.AssertionError is Sub assert cpy_builtin.compile == mycompile --- a/testing/code/test_excinfo.py Fri Jun 03 13:44:01 2011 -0500 +++ b/testing/code/test_excinfo.py Fri Jun 03 13:49:13 2011 -0500 @@ -193,15 +193,6 @@ def hello(x): x + 5 -def test_tbentry_reinterpret(): - try: - hello("hello") - except TypeError: - excinfo = py.code.ExceptionInfo() - tbentry = excinfo.traceback[-1] - msg = tbentry.reinterpret() - assert msg.startswith("TypeError: ('hello' + 5)") - def test_excinfo_exconly(): excinfo = py.test.raises(ValueError, h) assert excinfo.exconly().startswith('ValueError') @@ -595,27 +586,6 @@ assert reprtb.extraline == "!!! Recursion detected (same locals & position)" assert str(reprtb) - def test_tb_entry_AssertionError(self, importasmod): - # probably this test is a bit redundant - # as py/magic/testing/test_assertion.py - # already tests correctness of - # assertion-reinterpretation logic - mod = importasmod(""" - def somefunc(): - x = 1 - assert x == 2 - """) - py.code.patch_builtins(assertion=True) - try: - excinfo = py.test.raises(AssertionError, mod.somefunc) - finally: - py.code.unpatch_builtins(assertion=True) - - p = FormattedExcinfo() - reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) - lines = reprentry.lines - assert lines[-1] == "E assert 1 == 2" - def test_reprexcinfo_getrepr(self, importasmod): mod = importasmod(""" def f(x): http://bitbucket.org/hpk42/py/changeset/ab16cf6e7de3/ changeset: ab16cf6e7de3 branches: user: gutworth date: 2011-06-03 20:49:49 summary: merge heads affected #: 2 files (10 bytes) --- a/py/__init__.py Fri Jun 03 13:49:13 2011 -0500 +++ b/py/__init__.py Fri Jun 03 13:49:49 2011 -0500 @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.3' +__version__ = '1.4.4.dev1' from py import _apipkg --- a/setup.py Fri Jun 03 13:49:13 2011 -0500 +++ b/setup.py Fri Jun 03 13:49:49 2011 -0500 @@ -9,7 +9,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.3', + version='1.4.4.dev1', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Jun 4 05:10:49 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 04 Jun 2011 03:10:49 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20110604031049.17973.37324@bitbucket03.managed.contegix.com> 2 new changesets in pytest: http://bitbucket.org/hpk42/pytest/changeset/4d1a754b239d/ changeset: 4d1a754b239d branches: user: gutworth date: 2011-06-03 23:51:49 summary: fix assertion introspection on python 3.2+ affected #: 1 file (5 bytes) --- a/_pytest/assertion/__init__.py Wed Jun 01 15:08:54 2011 +0200 +++ b/_pytest/assertion/__init__.py Fri Jun 03 16:51:49 2011 -0500 @@ -68,8 +68,8 @@ def _write_pyc(co, source_path): if hasattr(imp, "cache_from_source"): # Handle PEP 3147 pycs. - pyc = py.path(imp.cache_from_source(source_math)) - pyc.dirname.ensure(dir=True) + pyc = py.path.local(imp.cache_from_source(str(source_path))) + pyc.ensure() else: pyc = source_path + "c" mtime = int(source_path.mtime()) http://bitbucket.org/hpk42/pytest/changeset/363cffc6297a/ changeset: 363cffc6297a branches: user: gutworth date: 2011-06-04 05:11:00 summary: account for quotes in error messages affected #: 2 files (4 bytes) --- a/testing/acceptance_test.py Fri Jun 03 16:51:49 2011 -0500 +++ b/testing/acceptance_test.py Fri Jun 03 22:11:00 2011 -0500 @@ -89,7 +89,7 @@ result = testdir.runpytest(p) result.stdout.fnmatch_lines([ #XXX on jython this fails: "> import import_fails", - "E ImportError: No module named does_not_work", + "E ImportError: No module named *does_not_work*", ]) assert result.ret == 1 --- a/testing/test_terminal.py Fri Jun 03 16:51:49 2011 -0500 +++ b/testing/test_terminal.py Fri Jun 03 22:11:00 2011 -0500 @@ -533,7 +533,7 @@ result = testdir.runpytest(*option.args) result.stdout.fnmatch_lines([ "> import xyz", - "E ImportError: No module named xyz", + "E ImportError: No module named *xyz*", "*1 error*", ]) Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Jun 13 00:07:10 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 12 Jun 2011 22:07:10 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20110612220710.27110.51216@bitbucket01.managed.contegix.com> 2 new changesets in pytest: http://bitbucket.org/hpk42/pytest/changeset/de3fc5db5bdc/ changeset: de3fc5db5bdc user: gutworth date: 2011-06-12 23:39:38 summary: fix spacing affected #: 1 file (2 bytes) --- a/doc/assert.txt Fri Jun 03 22:11:00 2011 -0500 +++ b/doc/assert.txt Sun Jun 12 16:39:38 2011 -0500 @@ -179,11 +179,9 @@ All assert introspection can be turned off by passing ``--assertmode=off``. .. versionadded:: 2.1 - Add assert rewriting as an alternate introspection technique. .. versionchanged:: 2.1 - Introduce the ``--assertmode`` option. Deprecate ``--no-assert`` and ``--nomagic``. http://bitbucket.org/hpk42/pytest/changeset/25738adb7ef3/ changeset: 25738adb7ef3 user: gutworth date: 2011-06-13 00:07:49 summary: some tweaks to allow pypy apptests to use newinterpret affected #: 1 file (32 bytes) --- a/_pytest/assertion/newinterpret.py Sun Jun 12 16:39:38 2011 -0500 +++ b/_pytest/assertion/newinterpret.py Sun Jun 12 17:07:49 2011 -0500 @@ -60,21 +60,18 @@ frame = py.code.Frame(sys._getframe(1)) return interpret(offending_line, frame) -def getfailure(failure): - explanation = util.format_explanation(failure.explanation) - value = failure.cause[1] +def getfailure(e): + explanation = util.format_explanation(e.explanation) + value = e.cause[1] if str(value): - lines = explanation.splitlines() - if not lines: - lines.append("") - lines[0] += " << %s" % (value,) - explanation = "\n".join(lines) - text = "%s: %s" % (failure.cause[0].__name__, explanation) - if text.startswith("AssertionError: assert "): + lines = explanation.split('\n') + lines[0] += " << %s" % (e.value,) + explanation = '\n'.join(lines) + text = "%s: %s" % (e.cause[0].__name__, explanation) + if text.startswith('AssertionError: assert '): text = text[16:] return text - operator_map = { ast.BitOr : "|", ast.BitXor : "^", @@ -156,7 +153,7 @@ except Exception: # have to assume it isn't local = False - if not local: + if not self.frame.is_true(local): return name.id, result return explanation, result @@ -176,7 +173,7 @@ except Exception: raise Failure(explanation) try: - if not result: + if not self.frame.is_true(result): break except KeyboardInterrupt: raise @@ -302,8 +299,8 @@ try: from_instance = self.frame.eval(co, __exprinfo_expr=source_result) except Exception: - from_instance = True - if from_instance: + from_instance = None + if from_instance is None or self.frame.is_true(from_instance): rep = self.frame.repr(result) pattern = "%s\n{%s = %s\n}" explanation = pattern % (rep, rep, explanation) @@ -312,10 +309,10 @@ def visit_Assert(self, assrt): test_explanation, test_result = self.visit(assrt.test) if test_explanation.startswith("False\n{False =") and \ - test_explanation.endswith("\n"): + test_explanation.endswith("\n}"): test_explanation = test_explanation[15:-2] explanation = "assert %s" % (test_explanation,) - if not test_result: + if not self.frame.is_true(test_result): try: raise BuiltinAssertionError except Exception: Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Jun 13 05:41:22 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 13 Jun 2011 03:41:22 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20110613034122.27113.67495@bitbucket01.managed.contegix.com> 2 new changesets in pytest: http://bitbucket.org/hpk42/pytest/changeset/cca282422755/ changeset: cca282422755 user: gutworth date: 2011-06-13 04:57:22 summary: treat local as a black box affected #: 1 file (16 bytes) --- a/_pytest/assertion/newinterpret.py Sun Jun 12 17:07:49 2011 -0500 +++ b/_pytest/assertion/newinterpret.py Sun Jun 12 21:57:22 2011 -0500 @@ -152,8 +152,8 @@ local = self.frame.eval(co) except Exception: # have to assume it isn't - local = False - if not self.frame.is_true(local): + local = None + if local is None or not self.frame.is_true(local): return name.id, result return explanation, result http://bitbucket.org/hpk42/pytest/changeset/1c35b5d118fa/ changeset: 1c35b5d118fa user: gutworth date: 2011-06-13 05:41:58 summary: put explanation simplification in format_explanation so everyone can benefit affected #: 4 files (1.2 KB) --- a/_pytest/assertion/newinterpret.py Sun Jun 12 21:57:22 2011 -0500 +++ b/_pytest/assertion/newinterpret.py Sun Jun 12 22:41:58 2011 -0500 @@ -308,9 +308,6 @@ def visit_Assert(self, assrt): test_explanation, test_result = self.visit(assrt.test) - if test_explanation.startswith("False\n{False =") and \ - test_explanation.endswith("\n}"): - test_explanation = test_explanation[15:-2] explanation = "assert %s" % (test_explanation,) if not self.frame.is_true(test_result): try: --- a/_pytest/assertion/oldinterpret.py Sun Jun 12 21:57:22 2011 -0500 +++ b/_pytest/assertion/oldinterpret.py Sun Jun 12 22:41:58 2011 -0500 @@ -384,10 +384,6 @@ def run(self, frame): test = Interpretable(self.test) test.eval(frame) - # simplify 'assert False where False = ...' - if (test.explanation.startswith('False\n{False = ') and - test.explanation.endswith('\n}')): - test.explanation = test.explanation[15:-2] # print the result as 'assert ' self.result = test.result self.explanation = 'assert ' + test.explanation --- a/_pytest/assertion/util.py Sun Jun 12 21:57:22 2011 -0500 +++ b/_pytest/assertion/util.py Sun Jun 12 22:41:58 2011 -0500 @@ -19,6 +19,28 @@ for when one explanation needs to span multiple lines, e.g. when displaying diffs. """ + # simplify 'assert False where False = ...' + where = 0 + while True: + start = where = explanation.find("False\n{False = ", where) + if where == -1: + break + level = 0 + for i, c in enumerate(explanation[start:]): + if c == "{": + level += 1 + elif c == "}": + level -= 1 + if not level: + break + else: + raise AssertionError("unbalanced braces: %r" % (explanation,)) + end = start + i + where = end + if explanation[end - 1] == '\n': + explanation = (explanation[:start] + explanation[start+15:end-1] + + explanation[end+1:]) + where -= 17 raw_lines = (explanation or '').split('\n') # escape newlines not followed by {, } and ~ lines = [raw_lines[0]] --- a/testing/test_assertrewrite.py Sun Jun 12 21:57:22 2011 -0500 +++ b/testing/test_assertrewrite.py Sun Jun 12 22:41:58 2011 -0500 @@ -164,24 +164,19 @@ ns = {"g" : g} def f(): assert g() - assert getmsg(f, ns) == """assert False - + where False = g()""" + assert getmsg(f, ns) == """assert g()""" def f(): assert g(1) - assert getmsg(f, ns) == """assert False - + where False = g(1)""" + assert getmsg(f, ns) == """assert g(1)""" def f(): assert g(1, 2) - assert getmsg(f, ns) == """assert False - + where False = g(1, 2)""" + assert getmsg(f, ns) == """assert g(1, 2)""" def f(): assert g(1, g=42) - assert getmsg(f, ns) == """assert False - + where False = g(1, g=42)""" + assert getmsg(f, ns) == """assert g(1, g=42)""" def f(): assert g(1, 3, g=23) - assert getmsg(f, ns) == """assert False - + where False = g(1, 3, g=23)""" + assert getmsg(f, ns) == """assert g(1, 3, g=23)""" def test_attribute(self): class X(object): @@ -194,8 +189,7 @@ def f(): x.a = False assert x.a - assert getmsg(f, ns) == """assert False - + where False = x.a""" + assert getmsg(f, ns) == """assert x.a""" def test_comparisons(self): def f(): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Jun 13 15:50:15 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 13 Jun 2011 13:50:15 -0000 Subject: [py-svn] commit/pytest: gutworth: fix name Message-ID: <20110613135015.9909.88981@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/04522717e199/ changeset: 04522717e199 user: gutworth date: 2011-06-13 15:50:50 summary: fix name affected #: 1 file (2 bytes) --- a/_pytest/assertion/newinterpret.py Sun Jun 12 22:41:58 2011 -0500 +++ b/_pytest/assertion/newinterpret.py Mon Jun 13 08:50:50 2011 -0500 @@ -65,7 +65,7 @@ value = e.cause[1] if str(value): lines = explanation.split('\n') - lines[0] += " << %s" % (e.value,) + lines[0] += " << %s" % (value,) explanation = '\n'.join(lines) text = "%s: %s" % (e.cause[0].__name__, explanation) if text.startswith('AssertionError: assert '): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Jun 15 07:50:45 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 15 Jun 2011 05:50:45 -0000 Subject: [py-svn] commit/pytest: hpk42: fix getting-started which claimed you need to avoid side effect in asserts Message-ID: <20110615055045.6186.15664@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/d7b5a083b1b4/ changeset: d7b5a083b1b4 user: hpk42 date: 2011-06-15 07:50:34 summary: fix getting-started which claimed you need to avoid side effect in asserts affected #: 2 files (502 bytes) --- a/doc/assert.txt Mon Jun 13 08:50:50 2011 -0500 +++ b/doc/assert.txt Wed Jun 15 07:50:34 2011 +0200 @@ -129,9 +129,12 @@ .. _assert-details: +.. _`assert introspection`: -Assertion introspection details -------------------------------- +Advanced assertion introspection +---------------------------------- + +.. versionadded:: 2.1 Reporting details about the failing assertion is achieved either by rewriting assert statements before they are run or re-evaluating the assert expression and --- a/doc/getting-started.txt Mon Jun 13 08:50:50 2011 -0500 +++ b/doc/getting-started.txt Wed Jun 15 07:50:34 2011 +0200 @@ -61,20 +61,10 @@ .. note:: - You can simply use the ``assert`` statement for asserting - expectations because intermediate values will be presented to you. - This is arguably easier than learning all the `the JUnit legacy - methods`_. - - However, there remains one caveat to using simple asserts: your - assertion expression should better be side-effect free. Because - after an assertion failed py.test will re-evaluate the expression - in order to present intermediate values. You will get a nice warning - and you can easily fix it: compute the value ahead of the assert and - then do the assertion. Or maybe just use the assert "explicit message" - syntax:: - - assert expr, "message" # show "message" if expr is not True + You can simply use the ``assert`` statement for asserting test + expectations. pytest's :ref:`assert introspection` will intelligently + report intermediate values of the assert expression freeing + you from the need to learn the many names of `JUnit legacy methods`_. .. _`the JUnit legacy methods`: http://docs.python.org/library/unittest.html#test-cases Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Jun 18 22:06:37 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 18 Jun 2011 20:06:37 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20110618200637.4613.66593@bitbucket01.managed.contegix.com> 2 new changesets in pytest: http://bitbucket.org/hpk42/pytest/changeset/68e8157654ab/ changeset: 68e8157654ab user: gutworth date: 2011-06-18 22:07:36 summary: note condition for introspection happening affected #: 1 file (106 bytes) --- a/doc/assert.txt Mon Jun 13 08:50:50 2011 -0500 +++ b/doc/assert.txt Sat Jun 18 15:07:36 2011 -0500 @@ -44,6 +44,9 @@ allows you to use the idiomatic python constructs without boilerplate code while not losing introspection information. +Assertion introspection is enabled only when a message is not manually provided +in the assert statement. + See :ref:`assert-details` for more information on assertion introspection. http://bitbucket.org/hpk42/pytest/changeset/2216dc60b2a6/ changeset: 2216dc60b2a6 user: gutworth date: 2011-06-18 22:09:50 summary: merge heads affected #: 2 files (502 bytes) --- a/doc/assert.txt Sat Jun 18 15:07:36 2011 -0500 +++ b/doc/assert.txt Sat Jun 18 15:09:50 2011 -0500 @@ -132,9 +132,12 @@ .. _assert-details: +.. _`assert introspection`: -Assertion introspection details -------------------------------- +Advanced assertion introspection +---------------------------------- + +.. versionadded:: 2.1 Reporting details about the failing assertion is achieved either by rewriting assert statements before they are run or re-evaluating the assert expression and --- a/doc/getting-started.txt Sat Jun 18 15:07:36 2011 -0500 +++ b/doc/getting-started.txt Sat Jun 18 15:09:50 2011 -0500 @@ -61,20 +61,10 @@ .. note:: - You can simply use the ``assert`` statement for asserting - expectations because intermediate values will be presented to you. - This is arguably easier than learning all the `the JUnit legacy - methods`_. - - However, there remains one caveat to using simple asserts: your - assertion expression should better be side-effect free. Because - after an assertion failed py.test will re-evaluate the expression - in order to present intermediate values. You will get a nice warning - and you can easily fix it: compute the value ahead of the assert and - then do the assertion. Or maybe just use the assert "explicit message" - syntax:: - - assert expr, "message" # show "message" if expr is not True + You can simply use the ``assert`` statement for asserting test + expectations. pytest's :ref:`assert introspection` will intelligently + report intermediate values of the assert expression freeing + you from the need to learn the many names of `JUnit legacy methods`_. .. _`the JUnit legacy methods`: http://docs.python.org/library/unittest.html#test-cases Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Jun 18 22:31:34 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 18 Jun 2011 20:31:34 -0000 Subject: [py-svn] commit/pytest: hpk42: some fixes and clarifications to assert docs Message-ID: <20110618203134.15781.9923@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/70be841b89ea/ changeset: 70be841b89ea user: hpk42 date: 2011-06-18 22:30:46 summary: some fixes and clarifications to assert docs affected #: 1 file (367 bytes) --- a/doc/assert.txt Sat Jun 18 15:09:50 2011 -0500 +++ b/doc/assert.txt Sat Jun 18 22:30:46 2011 +0200 @@ -19,7 +19,7 @@ assert f() == 4 to assert that your function returns a certain value. If this assertion fails -you will see the value of ``x``:: +you will see the return value of the function call:: $ py.test test_assert1.py =========================== test session starts ============================ @@ -40,16 +40,20 @@ ========================= 1 failed in 0.02 seconds ========================= py.test has support for showing the values of the most common subexpressions -including calls, attributes, comparisons, and binary and unary operators. This -allows you to use the idiomatic python constructs without boilerplate code while -not losing introspection information. +including calls, attributes, comparisons, and binary and unary +operators. (See :ref:`tbreportdemo`). This allows you to use the +idiomatic python constructs without boilerplate code while not losing +introspection information. -Assertion introspection is enabled only when a message is not manually provided -in the assert statement. +However, if you specify a message with the assertion like this:: + + assert a % 2 == 0, "value was odd, should be even" + +then no assertion introspection takes places at all and the message +will be simply shown in the traceback. See :ref:`assert-details` for more information on assertion introspection. - assertions about expected exceptions ------------------------------------------ @@ -139,15 +143,17 @@ .. versionadded:: 2.1 -Reporting details about the failing assertion is achieved either by rewriting + +Reporting details about a failing assertion is achieved either by rewriting assert statements before they are run or re-evaluating the assert expression and recording the intermediate values. Which technique is used depends on the location of the assert, py.test's configuration, and Python version being used -to run py.test. +to run py.test. However, for assert statements with a manually provided +message, i.e. ``assert expr, message``, no assertion introspection takes place and the manually provided message will be rendered in tracebacks. By default, if the Python version is greater than or equal to 2.6, py.test rewrites assert statements in test modules. Rewritten assert statements put -introspection information into the assertion failure message. Note py.test only +introspection information into the assertion failure message. py.test only rewrites test modules directly discovered by its test collection process, so asserts in supporting modules will not be rewritten. Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Jun 19 02:27:22 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 19 Jun 2011 00:27:22 -0000 Subject: [py-svn] commit/py: gutworth: pytest uses this function quite a lot, so let's not do an import Message-ID: <20110619002722.4610.75945@bitbucket01.managed.contegix.com> 1 new changeset in py: http://bitbucket.org/hpk42/py/changeset/3a7280b1ff87/ changeset: 3a7280b1ff87 user: gutworth date: 2011-06-19 02:30:54 summary: pytest uses this function quite a lot, so let's not do an import affected #: 1 file (21 bytes) --- a/py/_path/common.py Fri Jun 03 13:49:49 2011 -0500 +++ b/py/_path/common.py Sat Jun 18 19:30:54 2011 -0500 @@ -368,6 +368,5 @@ else: name = str(path) # path.strpath # XXX svn? pattern = '*' + path.sep + pattern - from fnmatch import fnmatch - return fnmatch(name, pattern) + return py.std.fnmatch.fnmatch(name, pattern) Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Jun 20 14:18:38 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 20 Jun 2011 12:18:38 -0000 Subject: [py-svn] commit/pytest: flub: Add pytest_assertrepr_compare() docs Message-ID: <20110620121838.15779.21300@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/8c0df2a3ee3a/ changeset: 8c0df2a3ee3a user: flub date: 2011-06-20 14:18:12 summary: Add pytest_assertrepr_compare() docs affected #: 1 file (3.9 KB) --- a/doc/assert.txt Sat Jun 18 22:30:46 2011 +0200 +++ b/doc/assert.txt Mon Jun 20 14:18:12 2011 +0200 @@ -197,8 +197,112 @@ Introduce the ``--assertmode`` option. Deprecate ``--no-assert`` and ``--nomagic``. -.. - Defining your own comparison - ---------------------------------------------- +Defining your own comparison +---------------------------------------------- + +As already shown in examples py.test is able to provide detailed +explanations when an assertial fails. E.g. when comparing a +dictionary it will show you which elements differ:: + + $ py.test example.py + ============================= test session starts ========================== + platform linux2 -- Python 2.7.1 -- pytest-2.0.3 + collected 1 items + + example.py F + + =================================== FAILURES =============================== + __________________________________ test_text _______________________________ + + def test_text(): + > assert {'foo': 0, 'bar': 1} == {'foo': 0, 'bar': 0} + E assert {'bar': 1, 'foo': 0} == {'bar': 0, 'foo': 0} + E - {'bar': 1, 'foo': 0} + E ? ^ + E + {'bar': 0, 'foo': 0} + E ? ^ + + example.py:2: AssertionError + =========================== 1 failed in 0.03 seconds ======================= + + +py.test has builtin knowledge about displaying detailed information +for a number of types. If the objects compared do not match those it +will fall back to a less detailed genric comparison, e.g.:: + + $ py.test example2.py + ============================= test session starts ========================== + platform linux2 -- Python 2.7.1 -- pytest-2.0.3 + collected 1 items + + example2.py F + + =================================== FAILURES =============================== + ________________________________ test_foo __________________________________ + + def test_foo(): + f = Foo(1) + g = Foo(2) + > assert f == g + E assert == + + example2.py:24: AssertionError + =========================== 1 failed in 0.03 seconds ======================= + + +The detailed builtin comparisons are currently only present for +strings, sequences, sets and dictionaries which are compared for +equality (``==``) and for strings with a ``not in`` comparison. +However it is possible to add your own detailed explanations using the +``pytest_assertrepr_compare`` hook. + +.. py:function:: pytest_assertrepr_compare(config, op, left, right) + + The *config* argument is a ``_pytest.config.Config`` + instance. *op* will be the comparison operator: ``==``, ``<``, + ``in``, etc. While *left* and *right* will contain the objects + which are being compared. + + The return value must be either *None* in case this hook does not + provide an explanation for the arguments passed in, or a list of + strings. Each string in the list will be regarded as a line and + displayed on a line by itself. The first line is slightly special + and is meant to be a summary with the detailed explanation + following on the other lines. + + +As an example consider adding following hook in a conftest.py which +provides an alternative explanation for the ``Foo`` type used above:: + + def pytest_assertrepr_compare(op, left, right): + if (not isinstance(left, example2.Foo) or + not isinstance(right, example2.Foo) or + op != '=='): + return None + return ['Comparing Foo instances:', + ' vals: %s != %s' % (left.val, right.val)] + +When re-running the above example after adding this in the conftest.py +file the output now contains a more detailed description:: + + $ python pytest.py example2.py + ============================= test session starts ========================== + platform linux2 -- Python 2.7.1 -- pytest-2.1.0.dev4 + collected 1 items + + example2.py F + + =================================== FAILURES =============================== + ___________________________________ test_foo _______________________________ + + def test_foo(): + f = Foo(1) + g = Foo(2) + > assert f == g + E assert Comparing Foo instances: + E vals: 1 != 2 + + example2.py:24: AssertionError + =========================== 1 failed in 0.03 seconds ======================= Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Jun 20 18:13:01 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 20 Jun 2011 16:13:01 -0000 Subject: [py-svn] commit/pytest: hpk42: refine and streamline Floris example for an assert_reprcompare hook. Message-ID: <20110620161301.15781.34678@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/df6b782c6c26/ changeset: df6b782c6c26 user: hpk42 date: 2011-06-20 18:12:48 summary: refine and streamline Floris example for an assert_reprcompare hook. affected #: 1 file (2.4 KB) --- a/doc/assert.txt Mon Jun 20 14:18:12 2011 +0200 +++ b/doc/assert.txt Mon Jun 20 18:12:48 2011 +0200 @@ -22,14 +22,14 @@ you will see the return value of the function call:: $ py.test test_assert1.py - =========================== test session starts ============================ - platform linux2 -- Python 2.6.6 -- pytest-2.0.3 + ============================= test session starts ============================== + platform darwin -- Python 2.7.0 -- pytest-2.1.0.dev4 collecting ... collected 1 items test_assert1.py F - ================================= FAILURES ================================= - ______________________________ test_function _______________________________ + =================================== FAILURES =================================== + ________________________________ test_function _________________________________ def test_function(): > assert f() == 4 @@ -37,7 +37,7 @@ E + where 3 = f() test_assert1.py:5: AssertionError - ========================= 1 failed in 0.02 seconds ========================= + =========================== 1 failed in 0.05 seconds =========================== py.test has support for showing the values of the most common subexpressions including calls, attributes, comparisons, and binary and unary @@ -104,14 +104,14 @@ if you run this module:: $ py.test test_assert2.py - =========================== test session starts ============================ - platform linux2 -- Python 2.6.6 -- pytest-2.0.3 + ============================= test session starts ============================== + platform darwin -- Python 2.7.0 -- pytest-2.1.0.dev4 collecting ... collected 1 items test_assert2.py F - ================================= FAILURES ================================= - ___________________________ test_set_comparison ____________________________ + =================================== FAILURES =================================== + _____________________________ test_set_comparison ______________________________ def test_set_comparison(): set1 = set("1308") @@ -124,7 +124,7 @@ E '5' test_assert2.py:5: AssertionError - ========================= 1 failed in 0.03 seconds ========================= + =========================== 1 failed in 0.05 seconds =========================== Special comparisons are done for a number of cases: @@ -134,6 +134,54 @@ See the :ref:`reporting demo ` for many more examples. +Defining your own assertion comparison +---------------------------------------------- + +It is possible to add your own detailed explanations by implementing +the ``pytest_assertrepr_compare`` hook. + +.. autofunction:: _pytest.hookspec.pytest_assertrepr_compare + +As an example consider adding the following hook in a conftest.py which +provides an alternative explanation for ``Foo`` objects:: + + # content of conftest.py + from test_foocompare import Foo + def pytest_assertrepr_compare(op, left, right): + if isinstance(left, Foo) and isinstance(right, Foo) and op == "==": + return ['Comparing Foo instances:', + ' vals: %s != %s' % (left.val, right.val)] + +now, given this test module:: + + # content of test_foocompare.py + class Foo: + def __init__(self, val): + self.val = val + + def test_compare(): + f1 = Foo(1) + f2 = Foo(2) + assert f1 == f2 + +you can run the test module and get the custom output defined in +the conftest file:: + + $ py.test -q test_foocompare.py + collecting ... collected 1 items + F + =================================== FAILURES =================================== + _________________________________ test_compare _________________________________ + + def test_compare(): + f1 = Foo(1) + f2 = Foo(2) + > assert f1 == f2 + E assert Comparing Foo instances: + E vals: 1 != 2 + + test_foocompare.py:8: AssertionError + 1 failed in 0.05 seconds .. _assert-details: .. _`assert introspection`: @@ -198,111 +246,3 @@ ``--nomagic``. -Defining your own comparison ----------------------------------------------- - - -As already shown in examples py.test is able to provide detailed -explanations when an assertial fails. E.g. when comparing a -dictionary it will show you which elements differ:: - - $ py.test example.py - ============================= test session starts ========================== - platform linux2 -- Python 2.7.1 -- pytest-2.0.3 - collected 1 items - - example.py F - - =================================== FAILURES =============================== - __________________________________ test_text _______________________________ - - def test_text(): - > assert {'foo': 0, 'bar': 1} == {'foo': 0, 'bar': 0} - E assert {'bar': 1, 'foo': 0} == {'bar': 0, 'foo': 0} - E - {'bar': 1, 'foo': 0} - E ? ^ - E + {'bar': 0, 'foo': 0} - E ? ^ - - example.py:2: AssertionError - =========================== 1 failed in 0.03 seconds ======================= - - -py.test has builtin knowledge about displaying detailed information -for a number of types. If the objects compared do not match those it -will fall back to a less detailed genric comparison, e.g.:: - - $ py.test example2.py - ============================= test session starts ========================== - platform linux2 -- Python 2.7.1 -- pytest-2.0.3 - collected 1 items - - example2.py F - - =================================== FAILURES =============================== - ________________________________ test_foo __________________________________ - - def test_foo(): - f = Foo(1) - g = Foo(2) - > assert f == g - E assert == - - example2.py:24: AssertionError - =========================== 1 failed in 0.03 seconds ======================= - - -The detailed builtin comparisons are currently only present for -strings, sequences, sets and dictionaries which are compared for -equality (``==``) and for strings with a ``not in`` comparison. -However it is possible to add your own detailed explanations using the -``pytest_assertrepr_compare`` hook. - -.. py:function:: pytest_assertrepr_compare(config, op, left, right) - - The *config* argument is a ``_pytest.config.Config`` - instance. *op* will be the comparison operator: ``==``, ``<``, - ``in``, etc. While *left* and *right* will contain the objects - which are being compared. - - The return value must be either *None* in case this hook does not - provide an explanation for the arguments passed in, or a list of - strings. Each string in the list will be regarded as a line and - displayed on a line by itself. The first line is slightly special - and is meant to be a summary with the detailed explanation - following on the other lines. - - -As an example consider adding following hook in a conftest.py which -provides an alternative explanation for the ``Foo`` type used above:: - - def pytest_assertrepr_compare(op, left, right): - if (not isinstance(left, example2.Foo) or - not isinstance(right, example2.Foo) or - op != '=='): - return None - return ['Comparing Foo instances:', - ' vals: %s != %s' % (left.val, right.val)] - -When re-running the above example after adding this in the conftest.py -file the output now contains a more detailed description:: - - $ python pytest.py example2.py - ============================= test session starts ========================== - platform linux2 -- Python 2.7.1 -- pytest-2.1.0.dev4 - collected 1 items - - example2.py F - - =================================== FAILURES =============================== - ___________________________________ test_foo _______________________________ - - def test_foo(): - f = Foo(1) - g = Foo(2) - > assert f == g - E assert Comparing Foo instances: - E vals: 1 != 2 - - example2.py:24: AssertionError - =========================== 1 failed in 0.03 seconds ======================= Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Tue Jun 28 17:39:08 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 28 Jun 2011 15:39:08 -0000 Subject: [py-svn] commit/pytest: gutworth: fix the rewriter on relative imports (fixes #58) Message-ID: <20110628153908.2551.61010@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/ad2f1532c8b8/ changeset: ad2f1532c8b8 user: gutworth date: 2011-06-28 17:39:11 summary: fix the rewriter on relative imports (fixes #58) affected #: 2 files (282 bytes) --- a/_pytest/assertion/rewrite.py Mon Jun 20 18:12:48 2011 +0200 +++ b/_pytest/assertion/rewrite.py Tue Jun 28 10:39:11 2011 -0500 @@ -109,8 +109,8 @@ return lineno += len(doc) - 1 expect_docstring = False - elif (not isinstance(item, ast.ImportFrom) or item.level > 0 and - item.identifier != "__future__"): + elif (not isinstance(item, ast.ImportFrom) or item.level > 0 or + item.module != "__future__"): lineno = item.lineno break pos += 1 --- a/testing/test_assertrewrite.py Mon Jun 20 18:12:48 2011 +0200 +++ b/testing/test_assertrewrite.py Tue Jun 28 10:39:11 2011 -0500 @@ -76,6 +76,13 @@ assert imp.lineno == 3 assert imp.col_offset == 0 assert isinstance(m.body[4], ast.Expr) + s = """from . import relative\nother_stuff""" + m = rewrite(s) + for imp in m.body[0:2]: + assert isinstance(imp, ast.Import) + assert imp.lineno == 1 + assert imp.col_offset == 0 + assert isinstance(m.body[3], ast.Expr) def test_dont_rewrite(self): s = """'PYTEST_DONT_REWRITE'\nassert 14""" Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Jun 29 03:21:23 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 29 Jun 2011 01:21:23 -0000 Subject: [py-svn] commit/pytest: gutworth: rewrite with proper short-circuting on boolean operators (fixes #57) Message-ID: <20110629012123.2550.81300@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/9dead0879575/ changeset: 9dead0879575 user: gutworth date: 2011-06-29 03:21:22 summary: rewrite with proper short-circuting on boolean operators (fixes #57) affected #: 2 files (1.2 KB) --- a/_pytest/assertion/rewrite.py Tue Jun 28 10:39:11 2011 -0500 +++ b/_pytest/assertion/rewrite.py Tue Jun 28 20:21:22 2011 -0500 @@ -17,13 +17,8 @@ _saferepr = py.io.saferepr from _pytest.assertion.util import format_explanation as _format_explanation -def _format_boolop(operands, explanations, is_or): - show_explanations = [] - for operand, expl in zip(operands, explanations): - show_explanations.append(expl) - if operand == is_or: - break - return "(" + (is_or and " or " or " and ").join(show_explanations) + ")" +def _format_boolop(explanations, is_or): + return "(" + (is_or and " or " or " and ").join(explanations) + ")" def _call_reprcompare(ops, results, expls, each_obj): for i, res, expl in zip(range(len(ops)), results, expls): @@ -143,7 +138,7 @@ """Get a new variable.""" # Use a character invalid in python identifiers to avoid clashing. name = "@py_assert" + str(next(self.variable_counter)) - self.variables.add(name) + self.variables[self.cond_chain].add(name) return name def assign(self, expr): @@ -172,6 +167,13 @@ self.explanation_specifiers[specifier] = expr return "%(" + specifier + ")s" + def enter_cond(self, cond, body): + self.statements.append(ast.If(cond, body, [])) + self.cond_chain += cond, + + def leave_cond(self, n=1): + self.cond_chain = self.cond_chain[:-n] + def push_format_context(self): self.explanation_specifiers = {} self.stack.append(self.explanation_specifiers) @@ -198,7 +200,8 @@ # There's already a message. Don't mess with it. return [assert_] self.statements = [] - self.variables = set() + self.cond_chain = () + self.variables = collections.defaultdict(set) self.variable_counter = itertools.count() self.stack = [] self.on_failure = [] @@ -220,11 +223,22 @@ else: raise_ = ast.Raise(exc, None, None) body.append(raise_) - # Delete temporary variables. - names = [ast.Name(name, ast.Del()) for name in self.variables] - if names: - delete = ast.Delete(names) - self.statements.append(delete) + # Delete temporary variables. This requires a bit cleverness about the + # order, so we don't delete variables that are themselves conditions for + # later variables. + for chain in sorted(self.variables, key=len, reverse=True): + if chain: + where = [] + if len(chain) > 1: + cond = ast.Boolop(ast.And(), chain) + else: + cond = chain[0] + self.statements.append(ast.If(cond, where, [])) + else: + where = self.statements + v = self.variables[chain] + names = [ast.Name(name, ast.Del()) for name in v] + where.append(ast.Delete(names)) # Fix line numbers. for stmt in self.statements: set_location(stmt, assert_.lineno, assert_.col_offset) @@ -240,21 +254,32 @@ return name, self.explanation_param(expr) def visit_BoolOp(self, boolop): - operands = [] - explanations = [] + res_var = self.variable() + expl_list = self.assign(ast.List([], ast.Load())) + app = ast.Attribute(expl_list, "append", ast.Load()) + is_or = isinstance(boolop.op, ast.Or) + body = save = self.statements + levels = len(boolop.values) - 1 self.push_format_context() - for operand in boolop.values: - res, explanation = self.visit(operand) - operands.append(res) - explanations.append(explanation) - expls = ast.Tuple([ast.Str(expl) for expl in explanations], ast.Load()) - is_or = ast.Num(isinstance(boolop.op, ast.Or)) - expl_template = self.helper("format_boolop", - ast.Tuple(operands, ast.Load()), expls, - is_or) + # Process each operand, short-circuting if needed. + for i, v in enumerate(boolop.values): + res, expl = self.visit(v) + body.append(ast.Assign([ast.Name(res_var, ast.Store())], res)) + call = ast.Call(app, [ast.Str(expl)], [], None, None) + body.append(ast.Expr(call)) + if i < levels: + inner = [] + cond = res + if is_or: + cond = ast.UnaryOp(ast.Not(), cond) + self.enter_cond(cond, inner) + self.statements = body = inner + # Leave all conditions. + self.leave_cond(levels) + self.statements = save + expl_template = self.helper("format_boolop", expl_list, ast.Num(is_or)) expl = self.pop_format_context(expl_template) - res = self.assign(ast.BoolOp(boolop.op, operands)) - return res, self.explanation_param(expl) + return ast.Name(res_var, ast.Load()), self.explanation_param(expl) def visit_UnaryOp(self, unary): pattern = unary_map[unary.op.__class__] --- a/testing/test_assertrewrite.py Tue Jun 28 10:39:11 2011 -0500 +++ b/testing/test_assertrewrite.py Tue Jun 28 20:21:22 2011 -0500 @@ -129,16 +129,23 @@ assert f or g assert getmsg(f) == "assert (False or False)" def f(): + f = g = False + assert not f and not g + getmsg(f, must_pass=True) + def f(): f = True g = False assert f or g getmsg(f, must_pass=True) def test_short_circut_evaluation(self): - pytest.xfail("complicated fix; I'm not sure if it's important") def f(): assert True or explode getmsg(f, must_pass=True) + def f(): + x = 1 + assert x == 1 or x == 2 + getmsg(f, must_pass=True) def test_unary_op(self): def f(): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Jun 29 04:11:52 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 29 Jun 2011 02:11:52 -0000 Subject: [py-svn] commit/pytest: gutworth: use a plain old list for queuing Message-ID: <20110629021152.21630.8187@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/59d84ab29fc4/ changeset: 59d84ab29fc4 user: gutworth date: 2011-06-29 04:11:56 summary: use a plain old list for queuing affected #: 1 file (23 bytes) --- a/_pytest/assertion/rewrite.py Tue Jun 28 20:21:22 2011 -0500 +++ b/_pytest/assertion/rewrite.py Tue Jun 28 21:11:56 2011 -0500 @@ -113,9 +113,9 @@ for alias in aliases] mod.body[pos:pos] = imports # Collect asserts. - nodes = collections.deque([mod]) + nodes = [mod] while nodes: - node = nodes.popleft() + node = nodes.pop() for name, field in ast.iter_fields(node): if isinstance(field, list): new = [] Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Jun 29 04:14:37 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 29 Jun 2011 02:14:37 -0000 Subject: [py-svn] commit/pytest: gutworth: rewrite test modules on import Message-ID: <20110629021437.23320.72706@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/2f337b6a5d09/ changeset: 2f337b6a5d09 user: gutworth date: 2011-06-29 04:13:12 summary: rewrite test modules on import affected #: 4 files (5.8 KB) --- a/_pytest/assertion/__init__.py Tue Jun 28 21:11:56 2011 -0500 +++ b/_pytest/assertion/__init__.py Tue Jun 28 21:13:12 2011 -0500 @@ -2,20 +2,12 @@ support for presenting detailed information in failing assertions. """ import py -import imp -import marshal -import struct import sys import pytest from _pytest.monkeypatch import monkeypatch -from _pytest.assertion import reinterpret, util +from _pytest.assertion import util -try: - from _pytest.assertion.rewrite import rewrite_asserts -except ImportError: - rewrite_asserts = None -else: - import ast +REWRITING_AVAILABLE = "_ast" in sys.builtin_module_names def pytest_addoption(parser): group = parser.getgroup("debugconfig") @@ -38,9 +30,9 @@ def __init__(self, config, mode): self.mode = mode self.trace = config.trace.root.get("assertion") + self.pycs = [] def pytest_configure(config): - warn_about_missing_assertion() mode = config.getvalue("assertmode") if config.getvalue("noassert") or config.getvalue("nomagic"): if mode not in ("off", "default"): @@ -48,7 +40,10 @@ mode = "off" elif mode == "default": mode = "on" + if mode == "on" and not REWRITING_AVAILABLE: + mode = "old" if mode != "off": + _load_modules(mode) def callbinrepr(op, left, right): hook_result = config.hook.pytest_assertrepr_compare( config=config, op=op, left=left, right=right) @@ -60,69 +55,55 @@ m.setattr(py.builtin.builtins, 'AssertionError', reinterpret.AssertionError) m.setattr(util, '_reprcompare', callbinrepr) - if mode == "on" and rewrite_asserts is None: - mode = "old" + hook = None + if mode == "on": + hook = rewrite.AssertionRewritingHook() + sys.meta_path.append(hook) + warn_about_missing_assertion(mode) config._assertstate = AssertionState(config, mode) + config._assertstate.hook = hook config._assertstate.trace("configured with mode set to %r" % (mode,)) -def _write_pyc(co, source_path): - if hasattr(imp, "cache_from_source"): - # Handle PEP 3147 pycs. - pyc = py.path.local(imp.cache_from_source(str(source_path))) - pyc.ensure() - else: - pyc = source_path + "c" - mtime = int(source_path.mtime()) - fp = pyc.open("wb") - try: - fp.write(imp.get_magic()) - fp.write(struct.pack(" 2 new changesets in pytest: http://bitbucket.org/hpk42/pytest/changeset/abc3befb8883/ changeset: abc3befb8883 user: gutworth date: 2011-06-29 16:44:04 summary: rename --assertmode choices to be more explicit affected #: 2 files (90 bytes) --- a/_pytest/assertion/__init__.py Tue Jun 28 21:13:12 2011 -0500 +++ b/_pytest/assertion/__init__.py Wed Jun 29 09:44:04 2011 -0500 @@ -12,13 +12,13 @@ def pytest_addoption(parser): group = parser.getgroup("debugconfig") group.addoption('--assertmode', action="store", dest="assertmode", - choices=("on", "old", "off", "default"), default="default", - metavar="on|old|off", + choices=("rewrite", "reinterp", "off", "default"), + default="default", metavar="off|reinterp|rewrite", help="""control assertion debugging tools. 'off' performs no assertion debugging. -'old' reinterprets the expressions in asserts to glean information. -'on' (the default) rewrites the assert statements in test modules to provide -sub-expression results.""") +'reinterp' reinterprets the expressions in asserts to glean information. +'rewrite' (the default) rewrites the assert statements in test modules on import +to provide sub-expression results.""") group.addoption('--no-assert', action="store_true", default=False, dest="noassert", help="DEPRECATED equivalent to --assertmode=off") group.addoption('--nomagic', action="store_true", default=False, @@ -39,9 +39,9 @@ raise pytest.UsageError("assertion options conflict") mode = "off" elif mode == "default": - mode = "on" + mode = "rewrite" if mode == "on" and not REWRITING_AVAILABLE: - mode = "old" + mode = "reinterp" if mode != "off": _load_modules(mode) def callbinrepr(op, left, right): @@ -56,7 +56,7 @@ reinterpret.AssertionError) m.setattr(util, '_reprcompare', callbinrepr) hook = None - if mode == "on": + if mode == "rewrite": hook = rewrite.AssertionRewritingHook() sys.meta_path.append(hook) warn_about_missing_assertion(mode) @@ -65,7 +65,7 @@ config._assertstate.trace("configured with mode set to %r" % (mode,)) def pytest_unconfigure(config): - if config._assertstate.mode == "on": + if config._assertstate.mode == "rewrite": rewrite._drain_pycs(config._assertstate) hook = config._assertstate.hook if hook is not None: @@ -77,7 +77,7 @@ hook.set_session(session) def pytest_sessionfinish(session): - if session.config._assertstate.mode == "on": + if session.config._assertstate.mode == "rewrite": rewrite._drain_pycs(session.config._assertstate) hook = session.config._assertstate.hook if hook is not None: @@ -87,7 +87,7 @@ """Lazily import assertion related code.""" global rewrite, reinterpret from _pytest.assertion import reinterpret - if mode == "on": + if mode == "rewrite": from _pytest.assertion import rewrite def warn_about_missing_assertion(mode): @@ -96,7 +96,7 @@ except AssertionError: pass else: - if mode == "on": + if mode == "rewrite": specifically = ("assertions which are not in test modules " "will be ignored") else: --- a/testing/test_assertion.py Tue Jun 28 21:13:12 2011 -0500 +++ b/testing/test_assertion.py Wed Jun 29 09:44:04 2011 -0500 @@ -182,7 +182,7 @@ for opt in off_options: result = testdir.runpytest(*opt) assert "3 == 4" not in result.stdout.str() - for mode in "on", "old": + for mode in "rewrite", "reinterp": for other_opt in off_options[:3]: opt = ("--assertmode=" + mode,) + other_opt result = testdir.runpytest(*opt) @@ -194,7 +194,7 @@ def test_in_old_mode(): assert "@py_builtins" not in globals() """) - result = testdir.runpytest("--assertmode=old") + result = testdir.runpytest("--assertmode=reinterp") assert result.ret == 0 def test_triple_quoted_string_issue113(testdir): http://bitbucket.org/hpk42/pytest/changeset/5a104e61c354/ changeset: 5a104e61c354 user: gutworth date: 2011-06-29 17:52:39 summary: update assert docs affected #: 1 file (48 bytes) --- a/doc/assert.txt Wed Jun 29 09:44:04 2011 -0500 +++ b/doc/assert.txt Wed Jun 29 10:52:39 2011 -0500 @@ -196,30 +196,32 @@ assert statements before they are run or re-evaluating the assert expression and recording the intermediate values. Which technique is used depends on the location of the assert, py.test's configuration, and Python version being used -to run py.test. However, for assert statements with a manually provided -message, i.e. ``assert expr, message``, no assertion introspection takes place and the manually provided message will be rendered in tracebacks. +to run py.test. Note that for assert statements with a manually provided +message, i.e. ``assert expr, message``, no assertion introspection takes place +and the manually provided message will be rendered in tracebacks. By default, if the Python version is greater than or equal to 2.6, py.test rewrites assert statements in test modules. Rewritten assert statements put introspection information into the assertion failure message. py.test only rewrites test modules directly discovered by its test collection process, so -asserts in supporting modules will not be rewritten. +asserts in supporting modules which are not themselves test modules will not be +rewritten. .. note:: - py.test rewrites test modules as it collects tests from them. It does this by - writing a new pyc file which Python loads when the test module is - imported. If the module has already been loaded (it is in sys.modules), - though, Python will not load the rewritten module. This means if a test - module imports another test module which has not already been rewritten, then - py.test will not be able to rewrite the second module. + py.test rewrites test modules on import. It does this by using an import hook + to write a new pyc files. Most of the time this works transparently. However, + if you are messing with import yourself, the import hook may interfere. If + this is the case, simply use ``--assertmode=reinterp`` or + ``--assertmode=off``. Additionally, rewriting will fail silently if it cannot + write new pycs, i.e. in a read-only filesystem or a zipfile. If an assert statement has not been rewritten or the Python version is less than 2.6, py.test falls back on assert reinterpretation. In assert reinterpretation, py.test walks the frame of the function containing the assert statement to discover sub-expression results of the failing assert statement. You can force py.test to always use assertion reinterpretation by passing the -``--assertmode=old`` option. +``--assertmode=reinterp`` option. Assert reinterpretation has a caveat not present with assert rewriting: If evaluating the assert expression has side effects you may get a warning that the @@ -244,5 +246,3 @@ .. versionchanged:: 2.1 Introduce the ``--assertmode`` option. Deprecate ``--no-assert`` and ``--nomagic``. - - Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Jun 29 21:59:17 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 29 Jun 2011 19:59:17 -0000 Subject: [py-svn] commit/pytest: 4 new changesets Message-ID: <20110629195917.21629.92166@bitbucket01.managed.contegix.com> 4 new changesets in pytest: http://bitbucket.org/hpk42/pytest/changeset/f04325787e45/ changeset: f04325787e45 user: gutworth date: 2011-06-29 19:16:47 summary: don't try to remove pycs twice affected #: 1 file (22 bytes) --- a/_pytest/assertion/rewrite.py Wed Jun 29 10:52:39 2011 -0500 +++ b/_pytest/assertion/rewrite.py Wed Jun 29 12:16:47 2011 -0500 @@ -103,6 +103,7 @@ state.trace("couldn't find pyc: %r" % (pyc,)) else: state.trace("removed pyc: %r" % (pyc,)) + del state.pycs[:] def _write_pyc(co, source_path, pyc): mtime = int(source_path.mtime()) http://bitbucket.org/hpk42/pytest/changeset/807b00f7bec3/ changeset: 807b00f7bec3 user: gutworth date: 2011-06-29 20:28:04 summary: adjust for new option affected #: 1 file (5 bytes) --- a/testing/test_assertion.py Wed Jun 29 12:16:47 2011 -0500 +++ b/testing/test_assertion.py Wed Jun 29 13:28:04 2011 -0500 @@ -120,7 +120,7 @@ expl = ' '.join(callequal('foo', 'bar')) assert 'raised in repr()' not in expl - at pytest.mark.skipif("config._assertstate.mode != 'on'") + at pytest.mark.skipif("config._assertstate.mode != 'rewrite'") def test_rewritten(): assert "@py_builtins" in globals() http://bitbucket.org/hpk42/pytest/changeset/bd4a738bbd71/ changeset: bd4a738bbd71 user: gutworth date: 2011-06-29 20:55:26 summary: we want second resolution on mtime affected #: 1 file (5 bytes) --- a/_pytest/assertion/rewrite.py Wed Jun 29 13:28:04 2011 -0500 +++ b/_pytest/assertion/rewrite.py Wed Jun 29 13:55:26 2011 -0500 @@ -147,7 +147,7 @@ def _use_cached_pyc(source, cache): try: - mtime = source.mtime() + mtime = int(source.mtime()) fp = cache.open("rb") try: data = fp.read(8) http://bitbucket.org/hpk42/pytest/changeset/dc3a2bda9df9/ changeset: dc3a2bda9df9 user: gutworth date: 2011-06-29 21:00:13 summary: simplify affected #: 1 file (56 bytes) --- a/_pytest/assertion/rewrite.py Wed Jun 29 13:55:26 2011 -0500 +++ b/_pytest/assertion/rewrite.py Wed Jun 29 14:00:13 2011 -0500 @@ -155,13 +155,10 @@ fp.close() except EnvironmentError: return False - if (len(data) != 8 or - data[:4] != imp.get_magic() or - struct.unpack("