[pypy-commit] pypy vendor/stdlib: remove everything thats not in the stdlib or tells about the version from the vendor/stdlib branch
RonnyPfannschmidt
noreply at buildbot.pypy.org
Sat Apr 14 10:01:27 CEST 2012
Author: Ronny Pfannschmidt <Ronny.Pfannschmidt at gmx.de>
Branch: vendor/stdlib
Changeset: r54351:93769ea57f9b
Date: 2012-04-14 09:54 +0200
http://bitbucket.org/pypy/pypy/changeset/93769ea57f9b/
Log: remove everything thats not in the stdlib or tells about the version
from the vendor/stdlib branch
diff too long, truncating to 10000 out of 1166718 lines
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
--- a/.gitignore
+++ /dev/null
@@ -1,21 +0,0 @@
-.hg
-.svn
-
-*.pyc
-*.pyo
-*~
-
-bin/pypy-c
-include/*.h
-lib_pypy/ctypes_config_cache/_[^_]*_*.py
-pypy/_cache
-pypy/doc/*.html
-pypy/doc/config/*.html
-pypy/doc/discussion/*.html
-pypy/module/cpyext/src/*.o
-pypy/module/cpyext/test/*.o
-pypy/module/test_lib_pypy/ctypes_tests/*.o
-pypy/translator/c/src/dtoa.o
-pypy/translator/goal/pypy-c
-pypy/translator/goal/target*-c
-release/
\ No newline at end of file
diff --git a/.hgignore b/.hgignore
deleted file mode 100644
--- a/.hgignore
+++ /dev/null
@@ -1,76 +0,0 @@
-syntax: glob
-*.py[co]
-*~
-.*.swp
-.idea
-.project
-.pydevproject
-
-syntax: regexp
-^testresult$
-^site-packages$
-^site-packages/.*$
-^site-packages/.*$
-^bin$
-^pypy/bin/pypy-c
-^pypy/module/cpyext/src/.+\.o$
-^pypy/module/cpyext/src/.+\.obj$
-^pypy/module/cpyext/test/.+\.errors$
-^pypy/module/cpyext/test/.+\.o$
-^pypy/module/cpyext/test/.+\.obj$
-^pypy/module/cpyext/test/.+\.manifest$
-^pypy/module/test_lib_pypy/ctypes_tests/.+\.o$
-^pypy/doc/.+\.html$
-^pypy/doc/config/.+\.rst$
-^pypy/doc/basicblock\.asc$
-^pypy/doc/.+\.svninfo$
-^pypy/translator/c/src/libffi_msvc/.+\.obj$
-^pypy/translator/c/src/libffi_msvc/.+\.dll$
-^pypy/translator/c/src/libffi_msvc/.+\.lib$
-^pypy/translator/c/src/libffi_msvc/.+\.exp$
-^pypy/translator/c/src/cjkcodecs/.+\.o$
-^pypy/translator/c/src/cjkcodecs/.+\.obj$
-^pypy/translator/jvm/\.project$
-^pypy/translator/jvm/\.classpath$
-^pypy/translator/jvm/eclipse-bin$
-^pypy/translator/jvm/src/pypy/.+\.class$
-^pypy/translator/benchmark/docutils$
-^pypy/translator/benchmark/templess$
-^pypy/translator/benchmark/gadfly$
-^pypy/translator/benchmark/mako$
-^pypy/translator/benchmark/bench-custom\.benchmark_result$
-^pypy/translator/benchmark/shootout_benchmarks$
-^pypy/translator/goal/pypy-translation-snapshot$
-^pypy/translator/goal/pypy-c
-^pypy/translator/goal/pypy-jvm
-^pypy/translator/goal/pypy-jvm.jar
-^pypy/translator/goal/.+\.exe$
-^pypy/translator/goal/.+\.dll$
-^pypy/translator/goal/target.+-c$
-^pypy/_cache$
-^pypy/doc/statistic/.+\.html$
-^pypy/doc/statistic/.+\.eps$
-^pypy/doc/statistic/.+\.pdf$
-^pypy/translator/cli/src/pypylib\.dll$
-^pypy/translator/cli/src/query\.exe$
-^pypy/translator/cli/src/main\.exe$
-^lib_pypy/ctypes_config_cache/_.+_cache\.py$
-^lib_pypy/ctypes_config_cache/_.+_.+_\.py$
-^pypy/translator/cli/query-descriptions$
-^pypy/doc/discussion/.+\.html$
-^include/.+\.h$
-^include/.+\.inl$
-^pypy/doc/_build/.*$
-^pypy/doc/config/.+\.html$
-^pypy/doc/config/style\.css$
-^pypy/doc/jit/.+\.html$
-^pypy/doc/jit/style\.css$
-^pypy/doc/image/lattice1\.png$
-^pypy/doc/image/lattice2\.png$
-^pypy/doc/image/lattice3\.png$
-^pypy/doc/image/stackless_informal\.png$
-^pypy/doc/image/parsing_example.+\.png$
-^pypy/module/test_lib_pypy/ctypes_tests/_ctypes_test\.o$
-^compiled
-^.git/
-^release/
diff --git a/.hgsubstate b/.hgsubstate
deleted file mode 100644
diff --git a/.hgtags b/.hgtags
deleted file mode 100644
--- a/.hgtags
+++ /dev/null
@@ -1,4 +0,0 @@
-b590cf6de4190623aad9aa698694c22e614d67b9 release-1.5
-b48df0bf4e75b81d98f19ce89d4a7dc3e1dab5e5 benchmarked
-d8ac7d23d3ec5f9a0fa1264972f74a010dbfd07f release-1.6
-ff4af8f318821f7f5ca998613a60fca09aa137da release-1.7
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
--- a/LICENSE
+++ /dev/null
@@ -1,270 +0,0 @@
-License for files in the pypy/ directory
-==================================================
-
-Except when otherwise stated (look for LICENSE files in directories or
-information at the beginning of each file) all software and
-documentation in the 'pypy' directories is licensed as follows:
-
- The MIT License
-
- Permission is hereby granted, free of charge, to any person
- obtaining a copy of this software and associated documentation
- files (the "Software"), to deal in the Software without
- restriction, including without limitation the rights to use,
- copy, modify, merge, publish, distribute, sublicense, and/or
- sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included
- in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
-
-PyPy Copyright holders 2003-2012
------------------------------------
-
-Except when otherwise stated (look for LICENSE files or information at
-the beginning of each file) the files in the 'pypy' directory are each
-copyrighted by one or more of the following people and organizations:
-
- Armin Rigo
- Maciej Fijalkowski
- Carl Friedrich Bolz
- Amaury Forgeot d'Arc
- Antonio Cuni
- Samuele Pedroni
- Michael Hudson
- Holger Krekel
- Alex Gaynor
- Christian Tismer
- Hakan Ardo
- Benjamin Peterson
- David Schneider
- Eric van Riet Paap
- Anders Chrigstrom
- Richard Emslie
- Dan Villiom Podlaski Christiansen
- Alexander Schremmer
- Lukas Diekmann
- Aurelien Campeas
- Anders Lehmann
- Camillo Bruni
- Niklaus Haldimann
- Sven Hager
- Leonardo Santagada
- Toon Verwaest
- Seo Sanghyeon
- Justin Peel
- Lawrence Oluyede
- Bartosz Skowron
- Jakub Gustak
- Guido Wesdorp
- Daniel Roberts
- Laura Creighton
- Adrien Di Mascio
- Ludovic Aubry
- Niko Matsakis
- Wim Lavrijsen
- Matti Picus
- Jason Creighton
- Jacob Hallen
- Alex Martelli
- Anders Hammarquist
- Jan de Mooij
- Stephan Diehl
- Michael Foord
- Stefan Schwarzer
- Tomek Meka
- Patrick Maupin
- Bob Ippolito
- Bruno Gola
- Alexandre Fayolle
- Marius Gedminas
- Simon Burton
- David Edelsohn
- Jean-Paul Calderone
- John Witulski
- Timo Paulssen
- holger krekel
- Dario Bertini
- Mark Pearse
- Andreas Stührk
- Jean-Philippe St. Pierre
- Guido van Rossum
- Pavel Vinogradov
- Valentino Volonghi
- Paul deGrandis
- Ilya Osadchiy
- Ronny Pfannschmidt
- Adrian Kuhn
- tav
- Georg Brandl
- Philip Jenvey
- Gerald Klix
- Wanja Saatkamp
- Boris Feigin
- Oscar Nierstrasz
- David Malcolm
- Eugene Oden
- Henry Mason
- Jeff Terrace
- Lukas Renggli
- Guenter Jantzen
- Ned Batchelder
- Bert Freudenberg
- Amit Regmi
- Ben Young
- Nicolas Chauvat
- Andrew Durdin
- Michael Schneider
- Nicholas Riley
- Rocco Moretti
- Gintautas Miliauskas
- Michael Twomey
- Igor Trindade Oliveira
- Lucian Branescu Mihaila
- Olivier Dormond
- Jared Grubb
- Karl Bartel
- Gabriel Lavoie
- Victor Stinner
- Brian Dorsey
- Stuart Williams
- Toby Watson
- Antoine Pitrou
- Justas Sadzevicius
- Neil Shepperd
- Mikael Schönenberg
- Gasper Zejn
- Jonathan David Riehl
- Elmo Mäntynen
- Anders Qvist
- Beatrice During
- Alexander Sedov
- Corbin Simpson
- Vincent Legoll
- Romain Guillebert
- Alan McIntyre
- Alex Perry
- Jens-Uwe Mager
- Simon Cross
- Dan Stromberg
- Guillebert Romain
- Carl Meyer
- Pieter Zieschang
- Alejandro J. Cura
- Sylvain Thenault
- Christoph Gerum
- Travis Francis Athougies
- Henrik Vendelbo
- Lutz Paelike
- Jacob Oscarson
- Martin Blais
- Lucio Torre
- Lene Wagner
- Miguel de Val Borro
- Artur Lisiecki
- Bruno Gola
- Ignas Mikalajunas
- Stefano Rivera
- Joshua Gilbert
- Godefroid Chappelle
- Yusei Tahara
- Christopher Armstrong
- Stephan Busemann
- Gustavo Niemeyer
- William Leslie
- Akira Li
- Kristjan Valur Jonsson
- Bobby Impollonia
- Michael Hudson-Doyle
- Laurence Tratt
- Yasir Suhail
- Andrew Thompson
- Anders Sigfridsson
- Floris Bruynooghe
- Jacek Generowicz
- Dan Colish
- Zooko Wilcox-O Hearn
- Dan Loewenherz
- Chris Lambacher
- Dinu Gherman
- Brett Cannon
- Daniel Neuhäuser
- Michael Chermside
- Konrad Delong
- Anna Ravencroft
- Greg Price
- Armin Ronacher
- Christian Muirhead
- Jim Baker
- Rodrigo Araújo
- Romain Guillebert
-
- Heinrich-Heine University, Germany
- Open End AB (formerly AB Strakt), Sweden
- merlinux GmbH, Germany
- tismerysoft GmbH, Germany
- Logilab Paris, France
- DFKI GmbH, Germany
- Impara, Germany
- Change Maker, Sweden
-
-The PyPy Logo as used by http://speed.pypy.org and others was created
-by Samuel Reis and is distributed on terms of Creative Commons Share Alike
-License.
-
-License for 'lib-python/2.7.0' and 'lib-python/2.7.0-modified'
-==============================================================
-
-Except when otherwise stated (look for LICENSE files or
-copyright/license information at the beginning of each file) the files
-in the 'lib-python/2.7.0' and 'lib-python/2.7.0-modified' directories
-are all copyrighted by the Python Software Foundation and licensed under
-the Python Software License of which you can find a copy here:
-http://www.python.org/doc/Copyright.html
-
-License for 'pypy/translator/jvm/src/jna.jar'
-=============================================
-
-The file 'pypy/translator/jvm/src/jna.jar' is licensed under the GNU
-Lesser General Public License of which you can find a copy here:
-http://www.gnu.org/licenses/lgpl.html
-
-License for 'pypy/translator/jvm/src/jasmin.jar'
-================================================
-
-The file 'pypy/translator/jvm/src/jasmin.jar' is copyright (c) 1996-2004 Jon Meyer
-and distributed with permission. The use of Jasmin by PyPy does not imply
-that PyPy is endorsed by Jon Meyer nor any of Jasmin's contributors. Furthermore,
-the following disclaimer applies to Jasmin:
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-License for 'pypy/module/unicodedata/'
-======================================
-
-The following files are from the website of The Unicode Consortium
-at http://www.unicode.org/. For the terms of use of these files, see
-http://www.unicode.org/terms_of_use.html . Or they are derived from
-files from the above website, and the same terms of use apply.
-
- CompositionExclusions-*.txt
- EastAsianWidth-*.txt
- LineBreak-*.txt
- UnicodeData-*.txt
- UnihanNumeric-*.txt
diff --git a/README b/README
deleted file mode 100644
--- a/README
+++ /dev/null
@@ -1,24 +0,0 @@
-=====================================
-PyPy: Python in Python Implementation
-=====================================
-
-Welcome to PyPy!
-
-PyPy is both an implementation of the Python programming language, and
-an extensive compiler framework for dynamic language implementations.
-You can build self-contained Python implementations which execute
-independently from CPython.
-
-The home page is:
-
- http://pypy.org/
-
-The getting-started document will help guide you:
-
- http://doc.pypy.org/en/latest/getting-started.html
-
-It will also point you to the rest of the documentation which is generated
-from files in the pypy/doc directory within the source repositories. Enjoy
-and send us feedback!
-
- the pypy-dev team <pypy-dev at python.org>
diff --git a/_pytest/__init__.py b/_pytest/__init__.py
deleted file mode 100644
--- a/_pytest/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-#
-__version__ = '2.2.4.dev2'
diff --git a/_pytest/assertion/__init__.py b/_pytest/assertion/__init__.py
deleted file mode 100644
--- a/_pytest/assertion/__init__.py
+++ /dev/null
@@ -1,119 +0,0 @@
-"""
-support for presenting detailed information in failing assertions.
-"""
-import py
-import sys
-import pytest
-from _pytest.monkeypatch import monkeypatch
-from _pytest.assertion import util
-
-def pytest_addoption(parser):
- group = parser.getgroup("debugconfig")
- group.addoption('--assert', action="store", dest="assertmode",
- choices=("rewrite", "reinterp", "plain",),
- default="rewrite", metavar="MODE",
- help="""control assertion debugging tools.
-'plain' performs no assertion debugging.
-'reinterp' reinterprets assert statements after they failed to provide assertion expression information.
-'rewrite' (the default) rewrites assert statements in test modules on import
-to provide assert expression information. """)
- group.addoption('--no-assert', action="store_true", default=False,
- dest="noassert", help="DEPRECATED equivalent to --assert=plain")
- group.addoption('--nomagic', action="store_true", default=False,
- dest="nomagic", help="DEPRECATED equivalent to --assert=plain")
-
-class AssertionState:
- """State for the assertion plugin."""
-
- def __init__(self, config, mode):
- self.mode = mode
- self.trace = config.trace.root.get("assertion")
-
-def pytest_configure(config):
- mode = config.getvalue("assertmode")
- if config.getvalue("noassert") or config.getvalue("nomagic"):
- mode = "plain"
- if mode == "rewrite":
- try:
- import ast
- except ImportError:
- mode = "reinterp"
- else:
- if sys.platform.startswith('java'):
- mode = "reinterp"
- if mode != "plain":
- _load_modules(mode)
- m = monkeypatch()
- config._cleanup.append(m.undo)
- m.setattr(py.builtin.builtins, 'AssertionError',
- reinterpret.AssertionError)
- hook = None
- if mode == "rewrite":
- 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 pytest_unconfigure(config):
- hook = config._assertstate.hook
- if hook is not None:
- sys.meta_path.remove(hook)
-
-def pytest_collection(session):
- # this hook is only called when test modules are collected
- # so for example not in the master process of pytest-xdist
- # (which does not collect test modules)
- hook = session.config._assertstate.hook
- if hook is not None:
- hook.set_session(session)
-
-def pytest_runtest_setup(item):
- def callbinrepr(op, left, right):
- hook_result = item.ihook.pytest_assertrepr_compare(
- config=item.config, op=op, left=left, right=right)
- for new_expl in hook_result:
- if new_expl:
- res = '\n~'.join(new_expl)
- if item.config.getvalue("assertmode") == "rewrite":
- # The result will be fed back a python % formatting
- # operation, which will fail if there are extraneous
- # '%'s in the string. Escape them here.
- res = res.replace("%", "%%")
- return res
- util._reprcompare = callbinrepr
-
-def pytest_runtest_teardown(item):
- util._reprcompare = None
-
-def pytest_sessionfinish(session):
- hook = session.config._assertstate.hook
- if hook is not None:
- hook.session = None
-
-def _load_modules(mode):
- """Lazily import assertion related code."""
- global rewrite, reinterpret
- from _pytest.assertion import reinterpret
- if mode == "rewrite":
- from _pytest.assertion import rewrite
-
-def warn_about_missing_assertion(mode):
- try:
- assert False
- except AssertionError:
- pass
- else:
- if mode == "rewrite":
- specifically = ("assertions which are not in test modules "
- "will be ignored")
- else:
- specifically = "failing tests may report as passing"
-
- sys.stderr.write("WARNING: " + specifically +
- " because assert statements are not executed "
- "by the underlying Python interpreter "
- "(are you using python -O?)\n")
-
-pytest_assertrepr_compare = util.assertrepr_compare
diff --git a/_pytest/assertion/newinterpret.py b/_pytest/assertion/newinterpret.py
deleted file mode 100644
--- a/_pytest/assertion/newinterpret.py
+++ /dev/null
@@ -1,333 +0,0 @@
-"""
-Find intermediate evalutation results in assert statements through builtin AST.
-This should replace oldinterpret.py eventually.
-"""
-
-import sys
-import ast
-
-import py
-from _pytest.assertion import util
-from _pytest.assertion.reinterpret import 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 --assert=plain)")
-
-def run(offending_line, frame=None):
- if frame is None:
- frame = py.code.Frame(sys._getframe(1))
- return interpret(offending_line, frame)
-
-def getfailure(e):
- explanation = util.format_explanation(e.explanation)
- value = e.cause[1]
- if str(value):
- lines = explanation.split('\n')
- lines[0] += " << %s" % (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 : "^",
- 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, "<assertion interpretation>", 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 = None
- if local is None or not self.frame.is_true(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 self.frame.is_true(result):
- break
- except KeyboardInterrupt:
- raise
- except:
- break
- left_explanation, left_result = next_explanation, next_result
-
- if util._reprcompare is not None:
- res = util._reprcompare(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 = 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)
- return explanation, result
-
- def visit_Assert(self, assrt):
- test_explanation, test_result = self.visit(assrt.test)
- explanation = "assert %s" % (test_explanation,)
- if not self.frame.is_true(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
diff --git a/_pytest/assertion/oldinterpret.py b/_pytest/assertion/oldinterpret.py
deleted file mode 100644
--- a/_pytest/assertion/oldinterpret.py
+++ /dev/null
@@ -1,552 +0,0 @@
-import py
-import sys, inspect
-from compiler import parse, ast, pycodegen
-from _pytest.assertion.util import format_explanation
-from _pytest.assertion.reinterpret import BuiltinAssertionError
-
-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 = '<eval>'
- self.__obj__.filename = '<eval>'
- 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 = '<run>'
- 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)
- # print the result as 'assert <explanation>'
- 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 = '<run>'
- 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 --assert=plain)")
- 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())")
diff --git a/_pytest/assertion/reinterpret.py b/_pytest/assertion/reinterpret.py
deleted file mode 100644
--- a/_pytest/assertion/reinterpret.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import sys
-import py
-
-BuiltinAssertionError = py.builtin.builtins.AssertionError
-
-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 "<run>".
- if source:
- self.msg = reinterpret(source, f, should_fail=True)
- else:
- self.msg = "<could not determine information>"
- 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 _pytest.assertion.oldinterpret import interpret as reinterpret_old
-if sys.version_info >= (2, 6) or (sys.platform.startswith("java")):
- from _pytest.assertion.newinterpret import interpret as reinterpret
-else:
- reinterpret = reinterpret_old
-
diff --git a/_pytest/assertion/rewrite.py b/_pytest/assertion/rewrite.py
deleted file mode 100644
--- a/_pytest/assertion/rewrite.py
+++ /dev/null
@@ -1,597 +0,0 @@
-"""Rewrite assertion AST to produce nice error messages"""
-
-import ast
-import errno
-import itertools
-import imp
-import marshal
-import os
-import struct
-import sys
-import types
-
-import py
-from _pytest.assertion import util
-
-
-# Windows gives ENOENT in places *nix gives ENOTDIR.
-if sys.platform.startswith("win"):
- PATH_COMPONENT_NOT_DIR = errno.ENOENT
-else:
- PATH_COMPONENT_NOT_DIR = errno.ENOTDIR
-
-# py.test caches rewritten pycs in __pycache__.
-if hasattr(imp, "get_tag"):
- PYTEST_TAG = imp.get_tag() + "-PYTEST"
-else:
- if hasattr(sys, "pypy_version_info"):
- impl = "pypy"
- elif sys.platform == "java":
- impl = "jython"
- else:
- impl = "cpython"
- ver = sys.version_info
- PYTEST_TAG = "%s-%s%s-PYTEST" % (impl, ver[0], ver[1])
- del ver, impl
-
-PYC_EXT = ".py" + "c" if __debug__ else "o"
-PYC_TAIL = "." + PYTEST_TAG + PYC_EXT
-
-REWRITE_NEWLINES = sys.version_info[:2] != (2, 7) and sys.version_info < (3, 2)
-
-class AssertionRewritingHook(object):
- """Import hook which rewrites asserts."""
-
- def __init__(self):
- self.session = None
- self.modules = {}
-
- def set_session(self, session):
- self.fnpats = session.config.getini("python_files")
- self.session = session
-
- def find_module(self, name, path=None):
- if self.session is None:
- return None
- sess = self.session
- state = sess.config._assertstate
- state.trace("find_module called for: %s" % name)
- names = name.rsplit(".", 1)
- lastname = names[-1]
- pth = None
- if path is not None and len(path) == 1:
- pth = path[0]
- if pth is None:
- try:
- fd, fn, desc = imp.find_module(lastname, path)
- except ImportError:
- return None
- if fd is not None:
- fd.close()
- tp = desc[2]
- if tp == imp.PY_COMPILED:
- if hasattr(imp, "source_from_cache"):
- fn = imp.source_from_cache(fn)
- else:
- fn = fn[:-1]
- elif tp != imp.PY_SOURCE:
- # Don't know what this is.
- return None
- else:
- fn = os.path.join(pth, name.rpartition(".")[2] + ".py")
- fn_pypath = py.path.local(fn)
- # Is this a test file?
- if not sess.isinitpath(fn):
- # We have to be very careful here because imports in this code can
- # trigger a cycle.
- self.session = None
- try:
- for pat in self.fnpats:
- if fn_pypath.fnmatch(pat):
- state.trace("matched test file %r" % (fn,))
- break
- else:
- return None
- finally:
- self.session = sess
- else:
- state.trace("matched test file (was specified on cmdline): %r" % (fn,))
- # The requested module looks like a test file, so rewrite it. This is
- # the most magical part of the process: load the source, rewrite the
- # asserts, and load the rewritten source. We also cache the rewritten
- # module code in a special pyc. We must be aware of the possibility of
- # concurrent py.test processes rewriting and loading pycs. To avoid
- # tricky race conditions, we maintain the following invariant: The
- # cached pyc is always a complete, valid pyc. Operations on it must be
- # atomic. POSIX's atomic rename comes in handy.
- write = not sys.dont_write_bytecode
- cache_dir = os.path.join(fn_pypath.dirname, "__pycache__")
- if write:
- try:
- os.mkdir(cache_dir)
- except OSError:
- e = sys.exc_info()[1].errno
- if e == errno.EEXIST:
- # Either the __pycache__ directory already exists (the
- # common case) or it's blocked by a non-dir node. In the
- # latter case, we'll ignore it in _write_pyc.
- pass
- elif e == PATH_COMPONENT_NOT_DIR:
- # One of the path components was not a directory, likely
- # because we're in a zip file.
- write = False
- elif e == errno.EACCES:
- state.trace("read only directory: %r" % (fn_pypath.dirname,))
- write = False
- else:
- raise
- cache_name = fn_pypath.basename[:-3] + PYC_TAIL
- pyc = os.path.join(cache_dir, cache_name)
- # Notice that even if we're in a read-only directory, I'm going to check
- # for a cached pyc. This may not be optimal...
- co = _read_pyc(fn_pypath, pyc)
- if co is None:
- state.trace("rewriting %r" % (fn,))
- co = _rewrite_test(state, fn_pypath)
- if co is None:
- # Probably a SyntaxError in the test.
- return None
- if write:
- _make_rewritten_pyc(state, fn_pypath, pyc, co)
- else:
- state.trace("found cached rewritten pyc for %r" % (fn,))
- self.modules[name] = co, pyc
- return self
-
- def load_module(self, name):
- co, pyc = self.modules.pop(name)
- # I wish I could just call imp.load_compiled here, but __file__ has to
- # be set properly. In Python 3.2+, this all would be handled correctly
- # by load_compiled.
- mod = sys.modules[name] = imp.new_module(name)
- try:
- mod.__file__ = co.co_filename
- # Normally, this attribute is 3.2+.
- mod.__cached__ = pyc
- py.builtin.exec_(co, mod.__dict__)
- except:
- del sys.modules[name]
- raise
- return sys.modules[name]
-
-def _write_pyc(co, source_path, pyc):
- # Technically, we don't have to have the same pyc format as (C)Python, since
- # these "pycs" should never be seen by builtin import. However, there's
- # little reason deviate, and I hope sometime to be able to use
- # imp.load_compiled to load them. (See the comment in load_module above.)
- mtime = int(source_path.mtime())
- try:
- fp = open(pyc, "wb")
- except IOError:
- err = sys.exc_info()[1].errno
- if err == PATH_COMPONENT_NOT_DIR:
- # This happens when we get a EEXIST in find_module creating the
- # __pycache__ directory and __pycache__ is by some non-dir node.
- return False
- raise
- try:
- fp.write(imp.get_magic())
- fp.write(struct.pack("<l", mtime))
- marshal.dump(co, fp)
- finally:
- fp.close()
- return True
-
-RN = "\r\n".encode("utf-8")
-N = "\n".encode("utf-8")
-
-def _rewrite_test(state, fn):
- """Try to read and rewrite *fn* and return the code object."""
- try:
- source = fn.read("rb")
- except EnvironmentError:
- return None
- # On Python versions which are not 2.7 and less than or equal to 3.1, the
- # parser expects *nix newlines.
- if REWRITE_NEWLINES:
- source = source.replace(RN, N) + N
- try:
- tree = ast.parse(source)
- except SyntaxError:
- # Let this pop up again in the real import.
- state.trace("failed to parse: %r" % (fn,))
- return None
- rewrite_asserts(tree)
- try:
- co = compile(tree, fn.strpath, "exec")
- except SyntaxError:
- # It's possible that this error is from some bug in the
- # assertion rewriting, but I don't know of a fast way to tell.
- state.trace("failed to compile: %r" % (fn,))
- return None
- return co
-
-def _make_rewritten_pyc(state, fn, pyc, co):
- """Try to dump rewritten code to *pyc*."""
- if sys.platform.startswith("win"):
- # Windows grants exclusive access to open files and doesn't have atomic
- # rename, so just write into the final file.
- _write_pyc(co, fn, pyc)
- else:
- # When not on windows, assume rename is atomic. Dump the code object
- # into a file specific to this process and atomically replace it.
- proc_pyc = pyc + "." + str(os.getpid())
- if _write_pyc(co, fn, proc_pyc):
- os.rename(proc_pyc, pyc)
-
-def _read_pyc(source, pyc):
- """Possibly read a py.test pyc containing rewritten code.
-
- Return rewritten code if successful or None if not.
- """
- try:
- fp = open(pyc, "rb")
- except IOError:
- return None
- try:
- try:
- mtime = int(source.mtime())
- data = fp.read(8)
- except EnvironmentError:
- return None
- # Check for invalid or out of date pyc file.
- if (len(data) != 8 or
- data[:4] != imp.get_magic() or
- struct.unpack("<l", data[4:])[0] != mtime):
- return None
- co = marshal.load(fp)
- if not isinstance(co, types.CodeType):
- # That's interesting....
- return None
- return co
- finally:
- fp.close()
-
-
-def rewrite_asserts(mod):
- """Rewrite the assert statements in mod."""
- AssertionRewriter().run(mod)
-
-
-_saferepr = py.io.saferepr
-from _pytest.assertion.util import format_explanation as _format_explanation
-
-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):
- try:
- done = not res
- except Exception:
- done = True
- if done:
- break
- if util._reprcompare is not None:
- custom = util._reprcompare(ops[i], each_obj[i], each_obj[i + 1])
- if custom is not None:
- return custom
- return expl
-
-
-unary_map = {
- ast.Not : "not %s",
- ast.Invert : "~%s",
- ast.USub : "-%s",
- ast.UAdd : "+%s"
-}
-
-binop_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"
-}
-
-
-def set_location(node, lineno, col_offset):
- """Set node location information recursively."""
- def _fix(node, lineno, col_offset):
- if "lineno" in node._attributes:
- node.lineno = lineno
- if "col_offset" in node._attributes:
- node.col_offset = col_offset
- for child in ast.iter_child_nodes(node):
- _fix(child, lineno, col_offset)
- _fix(node, lineno, col_offset)
- return node
-
-
-class AssertionRewriter(ast.NodeVisitor):
-
- def run(self, mod):
- """Find all assert statements in *mod* and rewrite them."""
- if not mod.body:
- # Nothing to do.
- return
- # Insert some special imports at the top of the module but after any
- # docstrings and __future__ imports.
- aliases = [ast.alias(py.builtin.builtins.__name__, "@py_builtins"),
- ast.alias("_pytest.assertion.rewrite", "@pytest_ar")]
- expect_docstring = True
- pos = 0
- lineno = 0
- for item in mod.body:
- if (expect_docstring and isinstance(item, ast.Expr) and
- isinstance(item.value, ast.Str)):
- doc = item.value.s
- if "PYTEST_DONT_REWRITE" in doc:
- # The module has disabled assertion rewriting.
- return
- lineno += len(doc) - 1
- expect_docstring = False
- elif (not isinstance(item, ast.ImportFrom) or item.level > 0 or
- item.module != "__future__"):
- lineno = item.lineno
- break
- pos += 1
- imports = [ast.Import([alias], lineno=lineno, col_offset=0)
- for alias in aliases]
- mod.body[pos:pos] = imports
- # Collect asserts.
- nodes = [mod]
- while nodes:
- node = nodes.pop()
- for name, field in ast.iter_fields(node):
- if isinstance(field, list):
- new = []
- for i, child in enumerate(field):
- if isinstance(child, ast.Assert):
- # Transform assert.
- new.extend(self.visit(child))
- else:
- new.append(child)
- if isinstance(child, ast.AST):
- nodes.append(child)
- setattr(node, name, new)
- elif (isinstance(field, ast.AST) and
- # Don't recurse into expressions as they can't contain
- # asserts.
- not isinstance(field, ast.expr)):
- nodes.append(field)
-
- def variable(self):
- """Get a new variable."""
- # Use a character invalid in python identifiers to avoid clashing.
- name = "@py_assert" + str(next(self.variable_counter))
- self.variables.append(name)
- return name
-
- def assign(self, expr):
- """Give *expr* a name."""
- name = self.variable()
- self.statements.append(ast.Assign([ast.Name(name, ast.Store())], expr))
- return ast.Name(name, ast.Load())
-
- def display(self, expr):
- """Call py.io.saferepr on the expression."""
- return self.helper("saferepr", expr)
-
- def helper(self, name, *args):
- """Call a helper in this module."""
- py_name = ast.Name("@pytest_ar", ast.Load())
- attr = ast.Attribute(py_name, "_" + name, ast.Load())
- return ast.Call(attr, list(args), [], None, None)
-
- def builtin(self, name):
- """Return the builtin called *name*."""
- builtin_name = ast.Name("@py_builtins", ast.Load())
- return ast.Attribute(builtin_name, name, ast.Load())
-
- def explanation_param(self, expr):
- specifier = "py" + str(next(self.variable_counter))
- self.explanation_specifiers[specifier] = expr
- return "%(" + specifier + ")s"
-
- def push_format_context(self):
- self.explanation_specifiers = {}
- self.stack.append(self.explanation_specifiers)
-
- def pop_format_context(self, expl_expr):
- current = self.stack.pop()
- if self.stack:
- self.explanation_specifiers = self.stack[-1]
- keys = [ast.Str(key) for key in current.keys()]
- format_dict = ast.Dict(keys, list(current.values()))
- form = ast.BinOp(expl_expr, ast.Mod(), format_dict)
- name = "@py_format" + str(next(self.variable_counter))
- self.on_failure.append(ast.Assign([ast.Name(name, ast.Store())], form))
- return ast.Name(name, ast.Load())
-
- def generic_visit(self, node):
- """Handle expressions we don't have custom code for."""
- assert isinstance(node, ast.expr)
- res = self.assign(node)
- return res, self.explanation_param(self.display(res))
-
- def visit_Assert(self, assert_):
- if assert_.msg:
- # There's already a message. Don't mess with it.
- return [assert_]
- self.statements = []
- self.cond_chain = ()
- self.variables = []
- self.variable_counter = itertools.count()
- self.stack = []
- self.on_failure = []
- self.push_format_context()
- # Rewrite assert into a bunch of statements.
- top_condition, explanation = self.visit(assert_.test)
- # Create failure message.
- body = self.on_failure
- negation = ast.UnaryOp(ast.Not(), top_condition)
- self.statements.append(ast.If(negation, body, []))
- explanation = "assert " + explanation
- template = ast.Str(explanation)
- msg = self.pop_format_context(template)
- fmt = self.helper("format_explanation", msg)
- err_name = ast.Name("AssertionError", ast.Load())
- exc = ast.Call(err_name, [fmt], [], None, None)
- if sys.version_info[0] >= 3:
- raise_ = ast.Raise(exc, None)
- else:
- raise_ = ast.Raise(exc, None, None)
- body.append(raise_)
- # Clear temporary variables by setting them to None.
- if self.variables:
- variables = [ast.Name(name, ast.Store()) for name in self.variables]
- clear = ast.Assign(variables, ast.Name("None", ast.Load()))
- self.statements.append(clear)
- # Fix line numbers.
- for stmt in self.statements:
- set_location(stmt, assert_.lineno, assert_.col_offset)
- return self.statements
-
- def visit_Name(self, name):
- # Check if the name is local or not.
- locs = ast.Call(self.builtin("locals"), [], [], None, None)
- globs = ast.Call(self.builtin("globals"), [], [], None, None)
- ops = [ast.In(), ast.IsNot()]
- test = ast.Compare(ast.Str(name.id), ops, [locs, globs])
- expr = ast.IfExp(test, self.display(name), ast.Str(name.id))
- return name, self.explanation_param(expr)
-
- def visit_BoolOp(self, boolop):
- res_var = self.variable()
- expl_list = self.assign(ast.List([], ast.Load()))
- app = ast.Attribute(expl_list, "append", ast.Load())
- is_or = int(isinstance(boolop.op, ast.Or))
- body = save = self.statements
- fail_save = self.on_failure
- levels = len(boolop.values) - 1
- self.push_format_context()
- # Process each operand, short-circuting if needed.
- for i, v in enumerate(boolop.values):
- if i:
- fail_inner = []
- self.on_failure.append(ast.If(cond, fail_inner, []))
- self.on_failure = fail_inner
- self.push_format_context()
- res, expl = self.visit(v)
- body.append(ast.Assign([ast.Name(res_var, ast.Store())], res))
- expl_format = self.pop_format_context(ast.Str(expl))
- call = ast.Call(app, [expl_format], [], None, None)
- self.on_failure.append(ast.Expr(call))
- if i < levels:
- cond = res
- if is_or:
- cond = ast.UnaryOp(ast.Not(), cond)
- inner = []
- self.statements.append(ast.If(cond, inner, []))
- self.statements = body = inner
- self.statements = save
- self.on_failure = fail_save
- expl_template = self.helper("format_boolop", expl_list, ast.Num(is_or))
- expl = self.pop_format_context(expl_template)
- return ast.Name(res_var, ast.Load()), self.explanation_param(expl)
-
- def visit_UnaryOp(self, unary):
- pattern = unary_map[unary.op.__class__]
- operand_res, operand_expl = self.visit(unary.operand)
- res = self.assign(ast.UnaryOp(unary.op, operand_res))
- return res, pattern % (operand_expl,)
-
- def visit_BinOp(self, binop):
- symbol = binop_map[binop.op.__class__]
- left_expr, left_expl = self.visit(binop.left)
- right_expr, right_expl = self.visit(binop.right)
- explanation = "(%s %s %s)" % (left_expl, symbol, right_expl)
- res = self.assign(ast.BinOp(left_expr, binop.op, right_expr))
- return res, explanation
-
- def visit_Call(self, call):
- new_func, func_expl = self.visit(call.func)
- arg_expls = []
- new_args = []
- new_kwargs = []
- new_star = new_kwarg = None
- for arg in call.args:
- res, expl = self.visit(arg)
- new_args.append(res)
- arg_expls.append(expl)
- for keyword in call.keywords:
- res, expl = self.visit(keyword.value)
- new_kwargs.append(ast.keyword(keyword.arg, res))
- arg_expls.append(keyword.arg + "=" + expl)
- if call.starargs:
- new_star, expl = self.visit(call.starargs)
- arg_expls.append("*" + expl)
- if call.kwargs:
- new_kwarg, expl = self.visit(call.kwargs)
- arg_expls.append("**" + expl)
- expl = "%s(%s)" % (func_expl, ', '.join(arg_expls))
- new_call = ast.Call(new_func, new_args, new_kwargs, new_star, new_kwarg)
- res = self.assign(new_call)
- res_expl = self.explanation_param(self.display(res))
- outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
- return res, outer_expl
-
- def visit_Attribute(self, attr):
- if not isinstance(attr.ctx, ast.Load):
- return self.generic_visit(attr)
- value, value_expl = self.visit(attr.value)
- res = self.assign(ast.Attribute(value, attr.attr, ast.Load()))
- res_expl = self.explanation_param(self.display(res))
- pat = "%s\n{%s = %s.%s\n}"
- expl = pat % (res_expl, res_expl, value_expl, attr.attr)
- return res, expl
-
- def visit_Compare(self, comp):
- self.push_format_context()
- left_res, left_expl = self.visit(comp.left)
- res_variables = [self.variable() for i in range(len(comp.ops))]
- load_names = [ast.Name(v, ast.Load()) for v in res_variables]
- store_names = [ast.Name(v, ast.Store()) for v in res_variables]
- it = zip(range(len(comp.ops)), comp.ops, comp.comparators)
- expls = []
- syms = []
- results = [left_res]
- for i, op, next_operand in it:
- next_res, next_expl = self.visit(next_operand)
- results.append(next_res)
- sym = binop_map[op.__class__]
- syms.append(ast.Str(sym))
- expl = "%s %s %s" % (left_expl, sym, next_expl)
- expls.append(ast.Str(expl))
- res_expr = ast.Compare(left_res, [op], [next_res])
- self.statements.append(ast.Assign([store_names[i]], res_expr))
- left_res, left_expl = next_res, next_expl
- # Use py.code._reprcompare if that's available.
- expl_call = self.helper("call_reprcompare",
- ast.Tuple(syms, ast.Load()),
- ast.Tuple(load_names, ast.Load()),
- ast.Tuple(expls, ast.Load()),
- ast.Tuple(results, ast.Load()))
- if len(comp.ops) > 1:
- res = ast.BoolOp(ast.And(), load_names)
- else:
- res = load_names[0]
- return res, self.explanation_param(self.pop_format_context(expl_call))
diff --git a/_pytest/assertion/util.py b/_pytest/assertion/util.py
deleted file mode 100644
--- a/_pytest/assertion/util.py
+++ /dev/null
@@ -1,213 +0,0 @@
-"""Utilities for assertion debugging"""
-
-import py
-
-
-# The _reprcompare attribute on the util module is used by the new assertion
-# interpretation code and assertion rewriter to detect this plugin was
-# loaded and in turn call the hooks defined here as part of the
-# DebugInterpreter.
-_reprcompare = None
-
-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.
- """
- # 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]]
- 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)
-
-
-# Provide basestring in python3
-try:
- basestring = basestring
-except NameError:
- basestring = str
-
-
-def assertrepr_compare(op, left, right):
- """return specialised explanations for some operators/operands"""
- width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op
- left_repr = py.io.saferepr(left, maxsize=int(width/2))
- right_repr = py.io.saferepr(right, maxsize=width-len(left_repr))
- summary = '%s %s %s' % (left_repr, op, right_repr)
-
- issequence = lambda x: isinstance(x, (list, tuple))
- istext = lambda x: isinstance(x, basestring)
- isdict = lambda x: isinstance(x, dict)
- isset = lambda x: isinstance(x, set)
-
- explanation = None
- try:
- if op == '==':
- if istext(left) and istext(right):
- explanation = _diff_text(left, right)
- elif issequence(left) and issequence(right):
- explanation = _compare_eq_sequence(left, right)
- elif isset(left) and isset(right):
- explanation = _compare_eq_set(left, right)
- elif isdict(left) and isdict(right):
- explanation = _diff_text(py.std.pprint.pformat(left),
- py.std.pprint.pformat(right))
- elif op == 'not in':
- if istext(left) and istext(right):
- explanation = _notin_text(left, right)
- except py.builtin._sysex:
- raise
- except:
- excinfo = py.code.ExceptionInfo()
- explanation = ['(pytest_assertion plugin: representation of '
- 'details failed. Probably an object has a faulty __repr__.)',
- str(excinfo)
- ]
-
-
- if not explanation:
- return None
-
- # Don't include pageloads of data, should be configurable
- if len(''.join(explanation)) > 80*8:
- explanation = ['Detailed information too verbose, truncated']
-
- return [summary] + explanation
-
-
-def _diff_text(left, right):
- """Return the explanation for the diff between text
-
- This will skip leading and trailing characters which are
- identical to keep the diff minimal.
- """
- explanation = []
- i = 0 # just in case left or right has zero length
- for i in range(min(len(left), len(right))):
- if left[i] != right[i]:
- break
- if i > 42:
- i -= 10 # Provide some context
- explanation = ['Skipping %s identical '
- 'leading characters in diff' % i]
- left = left[i:]
- right = right[i:]
- if len(left) == len(right):
- for i in range(len(left)):
- if left[-i] != right[-i]:
- break
- if i > 42:
- i -= 10 # Provide some context
- explanation += ['Skipping %s identical '
- 'trailing characters in diff' % i]
- left = left[:-i]
- right = right[:-i]
- explanation += [line.strip('\n')
- for line in py.std.difflib.ndiff(left.splitlines(),
- right.splitlines())]
- return explanation
-
-
-def _compare_eq_sequence(left, right):
- explanation = []
- for i in range(min(len(left), len(right))):
- if left[i] != right[i]:
- explanation += ['At index %s diff: %r != %r' %
- (i, left[i], right[i])]
- break
- if len(left) > len(right):
- explanation += ['Left contains more items, '
- 'first extra item: %s' % py.io.saferepr(left[len(right)],)]
- elif len(left) < len(right):
- explanation += ['Right contains more items, '
- 'first extra item: %s' % py.io.saferepr(right[len(left)],)]
- return explanation # + _diff_text(py.std.pprint.pformat(left),
- # py.std.pprint.pformat(right))
-
-
-def _compare_eq_set(left, right):
- explanation = []
- diff_left = left - right
- diff_right = right - left
- if diff_left:
- explanation.append('Extra items in the left set:')
- for item in diff_left:
- explanation.append(py.io.saferepr(item))
- if diff_right:
- explanation.append('Extra items in the right set:')
- for item in diff_right:
- explanation.append(py.io.saferepr(item))
- return explanation
-
-
-def _notin_text(term, text):
- index = text.find(term)
- head = text[:index]
- tail = text[index+len(term):]
- correct_text = head + tail
- diff = _diff_text(correct_text, text)
- newdiff = ['%s is contained here:' % py.io.saferepr(term, maxsize=42)]
- for line in diff:
- if line.startswith('Skipping'):
- continue
- if line.startswith('- '):
- continue
- if line.startswith('+ '):
- newdiff.append(' ' + line[2:])
- else:
- newdiff.append(line)
- return newdiff
diff --git a/_pytest/capture.py b/_pytest/capture.py
deleted file mode 100644
--- a/_pytest/capture.py
+++ /dev/null
@@ -1,214 +0,0 @@
-""" per-test stdout/stderr capturing mechanisms, ``capsys`` and ``capfd`` function arguments. """
-
-import pytest, py
-import os
-
-def pytest_addoption(parser):
- group = parser.getgroup("general")
- group._addoption('--capture', action="store", default=None,
- metavar="method", type="choice", choices=['fd', 'sys', 'no'],
- help="per-test capturing method: one of fd (default)|sys|no.")
- group._addoption('-s', action="store_const", const="no", dest="capture",
- help="shortcut for --capture=no.")
-
- at pytest.mark.tryfirst
-def pytest_cmdline_parse(pluginmanager, args):
- # we want to perform capturing already for plugin/conftest loading
- if '-s' in args or "--capture=no" in args:
- method = "no"
- elif hasattr(os, 'dup') and '--capture=sys' not in args:
- method = "fd"
- else:
- method = "sys"
- capman = CaptureManager(method)
- pluginmanager.register(capman, "capturemanager")
-
-def addouterr(rep, outerr):
- for secname, content in zip(["out", "err"], outerr):
- if content:
- rep.sections.append(("Captured std%s" % secname, content))
-
-class NoCapture:
- def startall(self):
- pass
- def resume(self):
- pass
- def reset(self):
- pass
- def suspend(self):
- return "", ""
-
-class CaptureManager:
- def __init__(self, defaultmethod=None):
- self._method2capture = {}
- self._defaultmethod = defaultmethod
-
- def _maketempfile(self):
- f = py.std.tempfile.TemporaryFile()
- newf = py.io.dupfile(f, encoding="UTF-8")
- f.close()
- return newf
-
- def _makestringio(self):
- return py.io.TextIO()
-
- def _getcapture(self, method):
- if method == "fd":
- return py.io.StdCaptureFD(now=False,
- out=self._maketempfile(), err=self._maketempfile()
- )
- elif method == "sys":
- return py.io.StdCapture(now=False,
- out=self._makestringio(), err=self._makestringio()
- )
- elif method == "no":
- return NoCapture()
- else:
- raise ValueError("unknown capturing method: %r" % method)
-
- def _getmethod(self, config, fspath):
- if config.option.capture:
- method = config.option.capture
- else:
- try:
- method = config._conftest.rget("option_capture", path=fspath)
- except KeyError:
- method = "fd"
- if method == "fd" and not hasattr(os, 'dup'): # e.g. jython
- method = "sys"
- return method
-
- def reset_capturings(self):
- for name, cap in self._method2capture.items():
- cap.reset()
-
- def resumecapture_item(self, item):
- method = self._getmethod(item.config, item.fspath)
- if not hasattr(item, 'outerr'):
- item.outerr = ('', '') # we accumulate outerr on the item
- return self.resumecapture(method)
-
- def resumecapture(self, method=None):
- if hasattr(self, '_capturing'):
- raise ValueError("cannot resume, already capturing with %r" %
- (self._capturing,))
- if method is None:
- method = self._defaultmethod
- cap = self._method2capture.get(method)
- self._capturing = method
- if cap is None:
- self._method2capture[method] = cap = self._getcapture(method)
- cap.startall()
- else:
- cap.resume()
-
- def suspendcapture(self, item=None):
- self.deactivate_funcargs()
- if hasattr(self, '_capturing'):
- method = self._capturing
- cap = self._method2capture.get(method)
- if cap is not None:
- outerr = cap.suspend()
- del self._capturing
- if item:
- outerr = (item.outerr[0] + outerr[0],
- item.outerr[1] + outerr[1])
- return outerr
- if hasattr(item, 'outerr'):
- return item.outerr
- return "", ""
-
- def activate_funcargs(self, pyfuncitem):
- if not hasattr(pyfuncitem, 'funcargs'):
- return
- assert not hasattr(self, '_capturing_funcargs')
- self._capturing_funcargs = capturing_funcargs = []
- for name, capfuncarg in pyfuncitem.funcargs.items():
- if name in ('capsys', 'capfd'):
- capturing_funcargs.append(capfuncarg)
- capfuncarg._start()
-
- def deactivate_funcargs(self):
- capturing_funcargs = getattr(self, '_capturing_funcargs', None)
- if capturing_funcargs is not None:
- while capturing_funcargs:
- capfuncarg = capturing_funcargs.pop()
- capfuncarg._finalize()
- del self._capturing_funcargs
-
- def pytest_make_collect_report(self, __multicall__, collector):
- method = self._getmethod(collector.config, collector.fspath)
- try:
- self.resumecapture(method)
- except ValueError:
- return # recursive collect, XXX refactor capturing
- # to allow for more lightweight recursive capturing
- try:
- rep = __multicall__.execute()
- finally:
- outerr = self.suspendcapture()
- addouterr(rep, outerr)
- return rep
-
- @pytest.mark.tryfirst
- def pytest_runtest_setup(self, item):
- self.resumecapture_item(item)
-
- @pytest.mark.tryfirst
- def pytest_runtest_call(self, item):
- self.resumecapture_item(item)
- self.activate_funcargs(item)
-
- @pytest.mark.tryfirst
- def pytest_runtest_teardown(self, item):
- self.resumecapture_item(item)
-
- def pytest_keyboard_interrupt(self, excinfo):
- if hasattr(self, '_capturing'):
- self.suspendcapture()
-
- @pytest.mark.tryfirst
- def pytest_runtest_makereport(self, __multicall__, item, call):
- self.deactivate_funcargs()
- rep = __multicall__.execute()
- outerr = self.suspendcapture(item)
- if not rep.passed:
- addouterr(rep, outerr)
- if not rep.passed or rep.when == "teardown":
- outerr = ('', '')
- item.outerr = outerr
- return rep
-
-def pytest_funcarg__capsys(request):
- """enables capturing of writes to sys.stdout/sys.stderr and makes
- captured output available via ``capsys.readouterr()`` method calls
- which return a ``(out, err)`` tuple.
- """
- return CaptureFuncarg(py.io.StdCapture)
-
-def pytest_funcarg__capfd(request):
- """enables capturing of writes to file descriptors 1 and 2 and makes
- captured output available via ``capsys.readouterr()`` method calls
- which return a ``(out, err)`` tuple.
- """
- if not hasattr(os, 'dup'):
- py.test.skip("capfd funcarg needs os.dup")
- return CaptureFuncarg(py.io.StdCaptureFD)
-
-class CaptureFuncarg:
- def __init__(self, captureclass):
- self.capture = captureclass(now=False)
-
- def _start(self):
- self.capture.startall()
-
- def _finalize(self):
- if hasattr(self, 'capture'):
- self.capture.reset()
- del self.capture
-
- def readouterr(self):
- return self.capture.readouterr()
-
- def close(self):
- self._finalize()
diff --git a/_pytest/config.py b/_pytest/config.py
deleted file mode 100644
--- a/_pytest/config.py
+++ /dev/null
@@ -1,463 +0,0 @@
-""" command line options, ini-file and conftest.py processing. """
-
-import py
-import sys, os
-from _pytest.core import PluginManager
-import pytest
-
-def pytest_cmdline_parse(pluginmanager, args):
- config = Config(pluginmanager)
- config.parse(args)
- return config
-
-def pytest_unconfigure(config):
- while 1:
- try:
- fin = config._cleanup.pop()
- except IndexError:
- break
- fin()
-
-class Parser:
- """ Parser for command line arguments. """
-
- def __init__(self, usage=None, processopt=None):
- self._anonymous = OptionGroup("custom options", parser=self)
- self._groups = []
- self._processopt = processopt
- self._usage = usage
- self._inidict = {}
- self._ininames = []
- self.hints = []
-
- def processoption(self, option):
- if self._processopt:
- if option.dest:
- self._processopt(option)
-
- def addnote(self, note):
- self._notes.append(note)
-
- def getgroup(self, name, description="", after=None):
- """ get (or create) a named option Group.
-
- :name: unique name of the option group.
- :description: long description for --help output.
- :after: name of other group, used for ordering --help output.
- """
- for group in self._groups:
- if group.name == name:
- return group
- group = OptionGroup(name, description, parser=self)
- i = 0
- for i, grp in enumerate(self._groups):
- if grp.name == after:
- break
- self._groups.insert(i+1, group)
- return group
-
- def addoption(self, *opts, **attrs):
- """ add an optparse-style option. """
- self._anonymous.addoption(*opts, **attrs)
-
- def parse(self, args):
- self.optparser = optparser = MyOptionParser(self)
- groups = self._groups + [self._anonymous]
- for group in groups:
- if group.options:
- desc = group.description or group.name
- optgroup = py.std.optparse.OptionGroup(optparser, desc)
- optgroup.add_options(group.options)
- optparser.add_option_group(optgroup)
- return self.optparser.parse_args([str(x) for x in args])
-
- def parse_setoption(self, args, option):
- parsedoption, args = self.parse(args)
- for name, value in parsedoption.__dict__.items():
- setattr(option, name, value)
- return args
-
- def addini(self, name, help, type=None, default=None):
- """ add an ini-file option with the given name and description. """
- assert type in (None, "pathlist", "args", "linelist")
- self._inidict[name] = (help, type, default)
- self._ininames.append(name)
-
-
-class OptionGroup:
- def __init__(self, name, description="", parser=None):
- self.name = name
- self.description = description
- self.options = []
- self.parser = parser
-
- def addoption(self, *optnames, **attrs):
- """ add an option to this group. """
- option = py.std.optparse.Option(*optnames, **attrs)
- self._addoption_instance(option, shortupper=False)
-
- def _addoption(self, *optnames, **attrs):
- option = py.std.optparse.Option(*optnames, **attrs)
- self._addoption_instance(option, shortupper=True)
-
- def _addoption_instance(self, option, shortupper=False):
- if not shortupper:
- for opt in option._short_opts:
- if opt[0] == '-' and opt[1].islower():
- raise ValueError("lowercase shortoptions reserved")
- if self.parser:
- self.parser.processoption(option)
- self.options.append(option)
-
-
-class MyOptionParser(py.std.optparse.OptionParser):
- def __init__(self, parser):
- self._parser = parser
- py.std.optparse.OptionParser.__init__(self, usage=parser._usage,
- add_help_option=False)
- def format_epilog(self, formatter):
- hints = self._parser.hints
- if hints:
- s = "\n".join(["hint: " + x for x in hints]) + "\n"
- s = "\n" + s + "\n"
- return s
- return ""
-
-class Conftest(object):
- """ the single place for accessing values and interacting
- towards conftest modules from py.test objects.
- """
- def __init__(self, onimport=None, confcutdir=None):
- self._path2confmods = {}
- self._onimport = onimport
- self._conftestpath2mod = {}
- self._confcutdir = confcutdir
-
- def setinitial(self, args):
- """ try to find a first anchor path for looking up global values
- from conftests. This function is usually called _before_
- argument parsing. conftest files may add command line options
- and we thus have no completely safe way of determining
- which parts of the arguments are actually related to options
- and which are file system paths. We just try here to get
- bootstrapped ...
- """
- current = py.path.local()
- opt = '--confcutdir'
- for i in range(len(args)):
- opt1 = str(args[i])
- if opt1.startswith(opt):
- if opt1 == opt:
- if len(args) > i:
- p = current.join(args[i+1], abs=True)
- elif opt1.startswith(opt + "="):
- p = current.join(opt1[len(opt)+1:], abs=1)
- self._confcutdir = p
- break
- for arg in args + [current]:
- if hasattr(arg, 'startswith') and arg.startswith("--"):
- continue
- anchor = current.join(arg, abs=1)
- if anchor.check(): # we found some file object
- self._path2confmods[None] = self.getconftestmodules(anchor)
- # let's also consider test* dirs
- if anchor.check(dir=1):
- for x in anchor.listdir("test*"):
- if x.check(dir=1):
- self.getconftestmodules(x)
- break
- else:
- assert 0, "no root of filesystem?"
-
- def getconftestmodules(self, path):
- """ return a list of imported conftest modules for the given path. """
- try:
- clist = self._path2confmods[path]
- except KeyError:
- if path is None:
- raise ValueError("missing default confest.")
- dp = path.dirpath()
- clist = []
- if dp != path:
- cutdir = self._confcutdir
- if cutdir and path != cutdir and not path.relto(cutdir):
- pass
- else:
- conftestpath = path.join("conftest.py")
- if conftestpath.check(file=1):
- clist.append(self.importconftest(conftestpath))
- clist[:0] = self.getconftestmodules(dp)
- self._path2confmods[path] = clist
- # be defensive: avoid changes from caller side to
- # affect us by always returning a copy of the actual list
- return clist[:]
-
- def rget(self, name, path=None):
- mod, value = self.rget_with_confmod(name, path)
- return value
-
- def rget_with_confmod(self, name, path=None):
- modules = self.getconftestmodules(path)
- modules.reverse()
- for mod in modules:
- try:
- return mod, getattr(mod, name)
- except AttributeError:
- continue
- raise KeyError(name)
-
- def importconftest(self, conftestpath):
- assert conftestpath.check(), conftestpath
- try:
- return self._conftestpath2mod[conftestpath]
- except KeyError:
- pkgpath = conftestpath.pypkgpath()
- if pkgpath is None:
- _ensure_removed_sysmodule(conftestpath.purebasename)
- self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport()
- dirpath = conftestpath.dirpath()
- if dirpath in self._path2confmods:
- for path, mods in self._path2confmods.items():
- if path and path.relto(dirpath) or path == dirpath:
- assert mod not in mods
- mods.append(mod)
- self._postimport(mod)
- return mod
-
- def _postimport(self, mod):
- if self._onimport:
- self._onimport(mod)
- return mod
-
-def _ensure_removed_sysmodule(modname):
- try:
- del sys.modules[modname]
- except KeyError:
- pass
-
-class CmdOptions(object):
- """ holds cmdline options as attributes."""
- def __init__(self, **kwargs):
- self.__dict__.update(kwargs)
- def __repr__(self):
- return "<CmdOptions %r>" %(self.__dict__,)
-
-class Config(object):
- """ access to configuration values, pluginmanager and plugin hooks. """
- def __init__(self, pluginmanager=None):
- #: command line option values, usually added via parser.addoption(...)
- #: or parser.getgroup(...).addoption(...) calls
- self.option = CmdOptions()
- self._parser = Parser(
- usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]",
- processopt=self._processopt,
- )
- #: a pluginmanager instance
- self.pluginmanager = pluginmanager or PluginManager(load=True)
- self.trace = self.pluginmanager.trace.root.get("config")
- self._conftest = Conftest(onimport=self._onimportconftest)
- self.hook = self.pluginmanager.hook
- self._inicache = {}
- self._cleanup = []
-
- @classmethod
- def fromdictargs(cls, option_dict, args):
- """ constructor useable for subprocesses. """
- config = cls()
- # XXX slightly crude way to initialize capturing
- import _pytest.capture
- _pytest.capture.pytest_cmdline_parse(config.pluginmanager, args)
- config._preparse(args, addopts=False)
- config.option.__dict__.update(option_dict)
- for x in config.option.plugins:
- config.pluginmanager.consider_pluginarg(x)
- return config
-
- def _onimportconftest(self, conftestmodule):
- self.trace("loaded conftestmodule %r" %(conftestmodule,))
- self.pluginmanager.consider_conftest(conftestmodule)
-
- def _processopt(self, opt):
- if hasattr(opt, 'default') and opt.dest:
- if not hasattr(self.option, opt.dest):
- setattr(self.option, opt.dest, opt.default)
-
- def _getmatchingplugins(self, fspath):
- allconftests = self._conftest._conftestpath2mod.values()
- plugins = [x for x in self.pluginmanager.getplugins()
- if x not in allconftests]
- plugins += self._conftest.getconftestmodules(fspath)
- return plugins
-
- def _setinitialconftest(self, args):
- # capture output during conftest init (#issue93)
- # XXX introduce load_conftest hook to avoid needing to know
- # about capturing plugin here
- capman = self.pluginmanager.getplugin("capturemanager")
- capman.resumecapture()
- try:
- try:
- self._conftest.setinitial(args)
- finally:
- out, err = capman.suspendcapture() # logging might have got it
- except:
- sys.stdout.write(out)
- sys.stderr.write(err)
- raise
-
- def _initini(self, args):
- self.inicfg = getcfg(args, ["pytest.ini", "tox.ini", "setup.cfg"])
- self._parser.addini('addopts', 'extra command line options', 'args')
- self._parser.addini('minversion', 'minimally required pytest version')
-
- def _preparse(self, args, addopts=True):
- self._initini(args)
- if addopts:
- args[:] = self.getini("addopts") + args
- self._checkversion()
- self.pluginmanager.consider_preparse(args)
- self.pluginmanager.consider_setuptools_entrypoints()
- self.pluginmanager.consider_env()
- self._setinitialconftest(args)
- self.pluginmanager.do_addoption(self._parser)
- if addopts:
- self.hook.pytest_cmdline_preparse(config=self, args=args)
-
- def _checkversion(self):
- minver = self.inicfg.get('minversion', None)
- if minver:
- ver = minver.split(".")
- myver = pytest.__version__.split(".")
- if myver < ver:
- raise pytest.UsageError(
- "%s:%d: requires pytest-%s, actual pytest-%s'" %(
- self.inicfg.config.path, self.inicfg.lineof('minversion'),
- minver, pytest.__version__))
-
- def parse(self, args):
- # parse given cmdline arguments into this config object.
- # Note that this can only be called once per testing process.
- assert not hasattr(self, 'args'), (
- "can only parse cmdline args at most once per Config object")
- self._origargs = args
- self._preparse(args)
- self._parser.hints.extend(self.pluginmanager._hints)
- args = self._parser.parse_setoption(args, self.option)
- if not args:
- args.append(py.std.os.getcwd())
- self.args = args
-
- def addinivalue_line(self, name, line):
- """ add a line to an ini-file option. The option must have been
- declared but might not yet be set in which case the line becomes the
- the first line in its value. """
- x = self.getini(name)
- assert isinstance(x, list)
- x.append(line) # modifies the cached list inline
-
- def getini(self, name):
- """ return configuration value from an ini file. If the
- specified name hasn't been registered through a prior ``parse.addini``
- call (usually from a plugin), a ValueError is raised. """
- try:
- return self._inicache[name]
- except KeyError:
- self._inicache[name] = val = self._getini(name)
- return val
-
- def _getini(self, name):
- try:
- description, type, default = self._parser._inidict[name]
- except KeyError:
- raise ValueError("unknown configuration value: %r" %(name,))
- try:
- value = self.inicfg[name]
- except KeyError:
- if default is not None:
- return default
- if type is None:
- return ''
- return []
- if type == "pathlist":
- dp = py.path.local(self.inicfg.config.path).dirpath()
- l = []
- for relpath in py.std.shlex.split(value):
- l.append(dp.join(relpath, abs=True))
- return l
- elif type == "args":
- return py.std.shlex.split(value)
- elif type == "linelist":
- return [t for t in map(lambda x: x.strip(), value.split("\n")) if t]
- else:
- assert type is None
- return value
-
- def _getconftest_pathlist(self, name, path=None):
- try:
- mod, relroots = self._conftest.rget_with_confmod(name, path)
- except KeyError:
- return None
- modpath = py.path.local(mod.__file__).dirpath()
- l = []
- for relroot in relroots:
- if not isinstance(relroot, py.path.local):
- relroot = relroot.replace("/", py.path.local.sep)
- relroot = modpath.join(relroot, abs=True)
- l.append(relroot)
- return l
-
- def _getconftest(self, name, path=None, check=False):
- if check:
- self._checkconftest(name)
- return self._conftest.rget(name, path)
-
- def getvalue(self, name, path=None):
- """ return ``name`` value looked set from command line options.
-
- (deprecated) if we can't find the option also lookup
- the name in a matching conftest file.
- """
- try:
- return getattr(self.option, name)
- except AttributeError:
- return self._getconftest(name, path, check=False)
-
- def getvalueorskip(self, name, path=None):
- """ (deprecated) return getvalue(name) or call
- py.test.skip if no value exists. """
- __tracebackhide__ = True
- try:
- val = self.getvalue(name, path)
- if val is None:
- raise KeyError(name)
- return val
- except KeyError:
- py.test.skip("no %r value found" %(name,))
-
-
-def getcfg(args, inibasenames):
- args = [x for x in args if not str(x).startswith("-")]
- if not args:
- args = [py.path.local()]
- for arg in args:
- arg = py.path.local(arg)
- for base in arg.parts(reverse=True):
- for inibasename in inibasenames:
- p = base.join(inibasename)
- if p.check():
- iniconfig = py.iniconfig.IniConfig(p)
- if 'pytest' in iniconfig.sections:
- return iniconfig['pytest']
- return {}
-
-def findupwards(current, basename):
- current = py.path.local(current)
- while 1:
- p = current.join(basename)
- if p.check():
- return p
- p = current.dirpath()
- if p == current:
- return
- current = p
-
diff --git a/_pytest/core.py b/_pytest/core.py
deleted file mode 100644
--- a/_pytest/core.py
+++ /dev/null
@@ -1,477 +0,0 @@
-"""
-pytest PluginManager, basic initialization and tracing.
-(c) Holger Krekel 2004-2010
-"""
-import sys, os
-import inspect
-import py
-from _pytest import hookspec # the extension point definitions
-
-assert py.__version__.split(".")[:2] >= ['1', '4'], ("installation problem: "
- "%s is too old, remove or upgrade 'py'" % (py.__version__))
-
-default_plugins = (
- "config mark main terminal runner python pdb unittest capture skipping "
- "tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
- "junitxml resultlog doctest").split()
-
-class TagTracer:
- def __init__(self):
- self._tag2proc = {}
- self.writer = None
- self.indent = 0
-
- def get(self, name):
- return TagTracerSub(self, (name,))
-
- def processmessage(self, tags, args):
- if self.writer is not None:
- if args:
- indent = " " * self.indent
- content = " ".join(map(str, args))
- self.writer("%s%s [%s]\n" %(indent, content, ":".join(tags)))
- try:
- self._tag2proc[tags](tags, args)
- except KeyError:
- pass
-
- def setwriter(self, writer):
- self.writer = writer
-
- def setprocessor(self, tags, processor):
- if isinstance(tags, str):
- tags = tuple(tags.split(":"))
- else:
- assert isinstance(tags, tuple)
- self._tag2proc[tags] = processor
-
-class TagTracerSub:
- def __init__(self, root, tags):
- self.root = root
- self.tags = tags
- def __call__(self, *args):
- self.root.processmessage(self.tags, args)
- def setmyprocessor(self, processor):
- self.root.setprocessor(self.tags, processor)
- def get(self, name):
- return self.__class__(self.root, self.tags + (name,))
-
-class PluginManager(object):
- def __init__(self, load=False):
- self._name2plugin = {}
- self._listattrcache = {}
- self._plugins = []
- self._hints = []
- self.trace = TagTracer().get("pluginmanage")
- self._plugin_distinfo = []
- if os.environ.get('PYTEST_DEBUG'):
- err = sys.stderr
- encoding = getattr(err, 'encoding', 'utf8')
- try:
- err = py.io.dupfile(err, encoding=encoding)
- except Exception:
- pass
- self.trace.root.setwriter(err.write)
- self.hook = HookRelay([hookspec], pm=self)
- self.register(self)
- if load:
- for spec in default_plugins:
- self.import_plugin(spec)
-
- def register(self, plugin, name=None, prepend=False):
- assert not self.isregistered(plugin), plugin
- name = name or getattr(plugin, '__name__', str(id(plugin)))
- if name in self._name2plugin:
- return False
- #self.trace("registering", name, plugin)
- self._name2plugin[name] = plugin
- self.call_plugin(plugin, "pytest_addhooks", {'pluginmanager': self})
- self.hook.pytest_plugin_registered(manager=self, plugin=plugin)
- if not prepend:
- self._plugins.append(plugin)
- else:
- self._plugins.insert(0, plugin)
- return True
-
- def unregister(self, plugin=None, name=None):
- if plugin is None:
- plugin = self.getplugin(name=name)
- self._plugins.remove(plugin)
- self.hook.pytest_plugin_unregistered(plugin=plugin)
- for name, value in list(self._name2plugin.items()):
- if value == plugin:
- del self._name2plugin[name]
-
- def isregistered(self, plugin, name=None):
- if self.getplugin(name) is not None:
- return True
- for val in self._name2plugin.values():
- if plugin == val:
- return True
-
- def addhooks(self, spec):
- self.hook._addhooks(spec, prefix="pytest_")
-
- def getplugins(self):
- return list(self._plugins)
-
- def skipifmissing(self, name):
- if not self.hasplugin(name):
- py.test.skip("plugin %r is missing" % name)
-
- def hasplugin(self, name):
- return bool(self.getplugin(name))
-
- def getplugin(self, name):
- if name is None:
- return None
- try:
- return self._name2plugin[name]
- except KeyError:
- return self._name2plugin.get("_pytest." + name, None)
-
- # API for bootstrapping
- #
- def _envlist(self, varname):
- val = py.std.os.environ.get(varname, None)
- if val is not None:
- return val.split(',')
- return ()
-
- def consider_env(self):
- for spec in self._envlist("PYTEST_PLUGINS"):
- self.import_plugin(spec)
-
- def consider_setuptools_entrypoints(self):
- try:
- from pkg_resources import iter_entry_points, DistributionNotFound
- except ImportError:
- return # XXX issue a warning
- for ep in iter_entry_points('pytest11'):
- name = ep.name
- if name.startswith("pytest_"):
- name = name[7:]
- if ep.name in self._name2plugin or name in self._name2plugin:
- continue
- try:
- plugin = ep.load()
- except DistributionNotFound:
- continue
- self._plugin_distinfo.append((ep.dist, plugin))
- self.register(plugin, name=name)
-
- def consider_preparse(self, args):
- for opt1,opt2 in zip(args, args[1:]):
- if opt1 == "-p":
- self.consider_pluginarg(opt2)
-
- def consider_pluginarg(self, arg):
- if arg.startswith("no:"):
- name = arg[3:]
- if self.getplugin(name) is not None:
- self.unregister(None, name=name)
- self._name2plugin[name] = -1
- else:
- if self.getplugin(arg) is None:
- self.import_plugin(arg)
-
- def consider_conftest(self, conftestmodule):
- if self.register(conftestmodule, name=conftestmodule.__file__):
- self.consider_module(conftestmodule)
-
- def consider_module(self, mod):
- attr = getattr(mod, "pytest_plugins", ())
- if attr:
- if not isinstance(attr, (list, tuple)):
- attr = (attr,)
- for spec in attr:
- self.import_plugin(spec)
-
- def import_plugin(self, modname):
- assert isinstance(modname, str)
- if self.getplugin(modname) is not None:
- return
- try:
- #self.trace("importing", modname)
- mod = importplugin(modname)
- except KeyboardInterrupt:
- raise
- except ImportError:
- if modname.startswith("pytest_"):
- return self.import_plugin(modname[7:])
- raise
- except:
- e = py.std.sys.exc_info()[1]
- if not hasattr(py.test, 'skip'):
- raise
- elif not isinstance(e, py.test.skip.Exception):
- raise
- self._hints.append("skipped plugin %r: %s" %((modname, e.msg)))
- else:
- self.register(mod, modname)
- self.consider_module(mod)
-
- def pytest_configure(self, config):
- config.addinivalue_line("markers",
- "tryfirst: mark a hook implementation function such that the "
- "plugin machinery will try to call it first/as early as possible.")
- config.addinivalue_line("markers",
- "trylast: mark a hook implementation function such that the "
- "plugin machinery will try to call it last/as late as possible.")
-
- def pytest_plugin_registered(self, plugin):
- import pytest
- dic = self.call_plugin(plugin, "pytest_namespace", {}) or {}
- if dic:
- self._setns(pytest, dic)
- if hasattr(self, '_config'):
- self.call_plugin(plugin, "pytest_addoption",
- {'parser': self._config._parser})
- self.call_plugin(plugin, "pytest_configure",
- {'config': self._config})
-
- def _setns(self, obj, dic):
- import pytest
- for name, value in dic.items():
- if isinstance(value, dict):
- mod = getattr(obj, name, None)
- if mod is None:
- modname = "pytest.%s" % name
- mod = py.std.types.ModuleType(modname)
- sys.modules[modname] = mod
- mod.__all__ = []
- setattr(obj, name, mod)
- obj.__all__.append(name)
- self._setns(mod, value)
- else:
- setattr(obj, name, value)
- obj.__all__.append(name)
- #if obj != pytest:
- # pytest.__all__.append(name)
- setattr(pytest, name, value)
-
- def pytest_terminal_summary(self, terminalreporter):
- tw = terminalreporter._tw
- if terminalreporter.config.option.traceconfig:
- for hint in self._hints:
- tw.line("hint: %s" % hint)
-
- def do_addoption(self, parser):
- mname = "pytest_addoption"
- methods = reversed(self.listattr(mname))
- MultiCall(methods, {'parser': parser}).execute()
-
- def do_configure(self, config):
- assert not hasattr(self, '_config')
- self._config = config
- config.hook.pytest_configure(config=self._config)
-
- def do_unconfigure(self, config):
- config = self._config
- del self._config
- config.hook.pytest_unconfigure(config=config)
- config.pluginmanager.unregister(self)
-
- def notify_exception(self, excinfo, option=None):
- if option and option.fulltrace:
- style = "long"
- else:
- style = "native"
- excrepr = excinfo.getrepr(funcargs=True,
- showlocals=getattr(option, 'showlocals', False),
- style=style,
- )
- res = self.hook.pytest_internalerror(excrepr=excrepr)
- if not py.builtin.any(res):
- for line in str(excrepr).split("\n"):
- sys.stderr.write("INTERNALERROR> %s\n" %line)
- sys.stderr.flush()
-
- def listattr(self, attrname, plugins=None):
- if plugins is None:
- plugins = self._plugins
- key = (attrname,) + tuple(plugins)
- try:
- return list(self._listattrcache[key])
- except KeyError:
- pass
- l = []
- last = []
- for plugin in plugins:
- try:
- meth = getattr(plugin, attrname)
- if hasattr(meth, 'tryfirst'):
- last.append(meth)
- elif hasattr(meth, 'trylast'):
- l.insert(0, meth)
- else:
- l.append(meth)
- except AttributeError:
- continue
- l.extend(last)
- self._listattrcache[key] = list(l)
- return l
-
- def call_plugin(self, plugin, methname, kwargs):
- return MultiCall(methods=self.listattr(methname, plugins=[plugin]),
- kwargs=kwargs, firstresult=True).execute()
-
-
-def importplugin(importspec):
- name = importspec
- try:
- mod = "_pytest." + name
- return __import__(mod, None, None, '__doc__')
- except ImportError:
- #e = py.std.sys.exc_info()[1]
- #if str(e).find(name) == -1:
- # raise
- pass #
- return __import__(importspec, None, None, '__doc__')
-
-class MultiCall:
- """ execute a call into multiple python functions/methods. """
- def __init__(self, methods, kwargs, firstresult=False):
- self.methods = list(methods)
- self.kwargs = kwargs
- self.results = []
- self.firstresult = firstresult
-
- def __repr__(self):
- status = "%d results, %d meths" % (len(self.results), len(self.methods))
- return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
-
- def execute(self):
- while self.methods:
- method = self.methods.pop()
- kwargs = self.getkwargs(method)
- res = method(**kwargs)
- if res is not None:
- self.results.append(res)
- if self.firstresult:
- return res
- if not self.firstresult:
- return self.results
-
- def getkwargs(self, method):
- kwargs = {}
- for argname in varnames(method):
- try:
- kwargs[argname] = self.kwargs[argname]
- except KeyError:
- if argname == "__multicall__":
- kwargs[argname] = self
- return kwargs
-
-def varnames(func):
- try:
- return func._varnames
- except AttributeError:
- pass
- if not inspect.isfunction(func) and not inspect.ismethod(func):
- func = getattr(func, '__call__', func)
- ismethod = inspect.ismethod(func)
- rawcode = py.code.getrawcode(func)
- try:
- x = rawcode.co_varnames[ismethod:rawcode.co_argcount]
- except AttributeError:
- x = ()
- py.builtin._getfuncdict(func)['_varnames'] = x
- return x
-
-class HookRelay:
- def __init__(self, hookspecs, pm, prefix="pytest_"):
- if not isinstance(hookspecs, list):
- hookspecs = [hookspecs]
- self._hookspecs = []
- self._pm = pm
- self.trace = pm.trace.root.get("hook")
- for hookspec in hookspecs:
- self._addhooks(hookspec, prefix)
-
- def _addhooks(self, hookspecs, prefix):
- self._hookspecs.append(hookspecs)
- added = False
- for name, method in vars(hookspecs).items():
- if name.startswith(prefix):
- firstresult = getattr(method, 'firstresult', False)
- hc = HookCaller(self, name, firstresult=firstresult)
- setattr(self, name, hc)
- added = True
- #print ("setting new hook", name)
- if not added:
- raise ValueError("did not find new %r hooks in %r" %(
- prefix, hookspecs,))
-
-
-class HookCaller:
- def __init__(self, hookrelay, name, firstresult):
- self.hookrelay = hookrelay
- self.name = name
- self.firstresult = firstresult
- self.trace = self.hookrelay.trace
-
- def __repr__(self):
- return "<HookCaller %r>" %(self.name,)
-
- def __call__(self, **kwargs):
- methods = self.hookrelay._pm.listattr(self.name)
- return self._docall(methods, kwargs)
-
- def pcall(self, plugins, **kwargs):
- methods = self.hookrelay._pm.listattr(self.name, plugins=plugins)
- return self._docall(methods, kwargs)
-
- def _docall(self, methods, kwargs):
- self.trace(self.name, kwargs)
- self.trace.root.indent += 1
- mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
- try:
- res = mc.execute()
- if res:
- self.trace("finish", self.name, "-->", res)
- finally:
- self.trace.root.indent -= 1
- return res
-
-_preinit = []
-
-def _preloadplugins():
- _preinit.append(PluginManager(load=True))
-
-def _prepareconfig(args=None, plugins=None):
- if args is None:
- args = sys.argv[1:]
- elif isinstance(args, py.path.local):
- args = [str(args)]
- elif not isinstance(args, (tuple, list)):
- if not isinstance(args, str):
- raise ValueError("not a string or argument list: %r" % (args,))
- args = py.std.shlex.split(args)
- if _preinit:
- _pluginmanager = _preinit.pop(0)
- else: # subsequent calls to main will create a fresh instance
- _pluginmanager = PluginManager(load=True)
- hook = _pluginmanager.hook
- if plugins:
- for plugin in plugins:
- _pluginmanager.register(plugin)
- return hook.pytest_cmdline_parse(
- pluginmanager=_pluginmanager, args=args)
-
-def main(args=None, plugins=None):
- """ returned exit code integer, after an in-process testing run
- with the given command line arguments, preloading an optional list
- of passed in plugin objects. """
- try:
- config = _prepareconfig(args, plugins)
- exitstatus = config.hook.pytest_cmdline_main(config=config)
- except UsageError:
- e = sys.exc_info()[1]
- sys.stderr.write("ERROR: %s\n" %(e.args[0],))
- exitstatus = 3
- return exitstatus
-
-class UsageError(Exception):
- """ error in py.test usage or invocation"""
-
diff --git a/_pytest/doctest.py b/_pytest/doctest.py
deleted file mode 100644
--- a/_pytest/doctest.py
+++ /dev/null
@@ -1,87 +0,0 @@
-""" discover and run doctests in modules and test files."""
-
-import pytest, py
-from py._code.code import TerminalRepr, ReprFileLocation
-
-def pytest_addoption(parser):
- group = parser.getgroup("collect")
- group.addoption("--doctest-modules",
- action="store_true", default=False,
- help="run doctests in all .py modules",
- dest="doctestmodules")
- group.addoption("--doctest-glob",
- action="store", default="test*.txt", metavar="pat",
- help="doctests file matching pattern, default: test*.txt",
- dest="doctestglob")
-
-def pytest_collect_file(path, parent):
- config = parent.config
- if path.ext == ".py":
- if config.option.doctestmodules:
- return DoctestModule(path, parent)
- elif (path.ext in ('.txt', '.rst') and parent.session.isinitpath(path)) or \
- path.check(fnmatch=config.getvalue("doctestglob")):
- return DoctestTextfile(path, parent)
-
-class ReprFailDoctest(TerminalRepr):
- def __init__(self, reprlocation, lines):
- self.reprlocation = reprlocation
- self.lines = lines
- def toterminal(self, tw):
- for line in self.lines:
- tw.line(line)
- self.reprlocation.toterminal(tw)
-
-class DoctestItem(pytest.Item):
- def repr_failure(self, excinfo):
- doctest = py.std.doctest
- if excinfo.errisinstance((doctest.DocTestFailure,
- doctest.UnexpectedException)):
- doctestfailure = excinfo.value
- example = doctestfailure.example
- test = doctestfailure.test
- filename = test.filename
- lineno = test.lineno + example.lineno + 1
- message = excinfo.type.__name__
- reprlocation = ReprFileLocation(filename, lineno, message)
- checker = py.std.doctest.OutputChecker()
- REPORT_UDIFF = py.std.doctest.REPORT_UDIFF
- filelines = py.path.local(filename).readlines(cr=0)
- i = max(test.lineno, max(0, lineno - 10)) # XXX?
- lines = []
- for line in filelines[i:lineno]:
- lines.append("%03d %s" % (i+1, line))
- i += 1
- if excinfo.errisinstance(doctest.DocTestFailure):
- lines += checker.output_difference(example,
- doctestfailure.got, REPORT_UDIFF).split("\n")
- else:
- inner_excinfo = py.code.ExceptionInfo(excinfo.value.exc_info)
- lines += ["UNEXPECTED EXCEPTION: %s" %
- repr(inner_excinfo.value)]
- lines += py.std.traceback.format_exception(*excinfo.value.exc_info)
- return ReprFailDoctest(reprlocation, lines)
- else:
- return super(DoctestItem, self).repr_failure(excinfo)
-
- def reportinfo(self):
- return self.fspath, None, "[doctest]"
-
-class DoctestTextfile(DoctestItem, pytest.File):
- def runtest(self):
- doctest = py.std.doctest
- failed, tot = doctest.testfile(
- str(self.fspath), module_relative=False,
- optionflags=doctest.ELLIPSIS,
- raise_on_error=True, verbose=0)
-
-class DoctestModule(DoctestItem, pytest.File):
- def runtest(self):
- doctest = py.std.doctest
- if self.fspath.basename == "conftest.py":
- module = self.config._conftest.importconftest(self.fspath)
- else:
- module = self.fspath.pyimport()
- failed, tot = doctest.testmod(
- module, raise_on_error=True, verbose=0,
- optionflags=doctest.ELLIPSIS)
diff --git a/_pytest/genscript.py b/_pytest/genscript.py
deleted file mode 100755
--- a/_pytest/genscript.py
+++ /dev/null
@@ -1,69 +0,0 @@
-""" generate a single-file self-contained version of py.test """
-import py
-
-def find_toplevel(name):
- for syspath in py.std.sys.path:
- base = py.path.local(syspath)
- lib = base/name
- if lib.check(dir=1):
- return lib
- mod = base.join("%s.py" % name)
- if mod.check(file=1):
- return mod
- raise LookupError(name)
-
-def pkgname(toplevel, rootpath, path):
- parts = path.parts()[len(rootpath.parts()):]
- return '.'.join([toplevel] + [x.purebasename for x in parts])
-
-def pkg_to_mapping(name):
- toplevel = find_toplevel(name)
- name2src = {}
- if toplevel.check(file=1): # module
- name2src[toplevel.purebasename] = toplevel.read()
- else: # package
- for pyfile in toplevel.visit('*.py'):
- pkg = pkgname(name, toplevel, pyfile)
- name2src[pkg] = pyfile.read()
- return name2src
-
-def compress_mapping(mapping):
- data = py.std.pickle.dumps(mapping, 2)
- data = py.std.zlib.compress(data, 9)
- data = py.std.base64.encodestring(data)
- data = data.decode('ascii')
- return data
-
-
-def compress_packages(names):
- mapping = {}
- for name in names:
- mapping.update(pkg_to_mapping(name))
- return compress_mapping(mapping)
-
-def generate_script(entry, packages):
- data = compress_packages(packages)
- tmpl = py.path.local(__file__).dirpath().join('standalonetemplate.py')
- exe = tmpl.read()
- exe = exe.replace('@SOURCES@', data)
- exe = exe.replace('@ENTRY@', entry)
- return exe
-
-
-def pytest_addoption(parser):
- group = parser.getgroup("debugconfig")
- group.addoption("--genscript", action="store", default=None,
- dest="genscript", metavar="path",
- help="create standalone py.test script at given target path.")
-
-def pytest_cmdline_main(config):
- genscript = config.getvalue("genscript")
- if genscript:
- script = generate_script(
- 'import py; raise SystemExit(py.test.cmdline.main())',
- ['py', '_pytest', 'pytest'],
- )
-
- genscript = py.path.local(genscript)
- genscript.write(script)
- return 0
diff --git a/_pytest/helpconfig.py b/_pytest/helpconfig.py
deleted file mode 100644
--- a/_pytest/helpconfig.py
+++ /dev/null
@@ -1,197 +0,0 @@
-""" version info, help messages, tracing configuration. """
-import py
-import pytest
-import os, inspect, sys
-from _pytest.core import varnames
-
-def pytest_addoption(parser):
- group = parser.getgroup('debugconfig')
- group.addoption('--version', action="store_true",
- help="display pytest lib version and import information.")
- group._addoption("-h", "--help", action="store_true", dest="help",
- help="show help message and configuration info")
- group._addoption('-p', action="append", dest="plugins", default = [],
- metavar="name",
- help="early-load given plugin (multi-allowed).")
- group.addoption('--traceconfig',
- action="store_true", dest="traceconfig", default=False,
- help="trace considerations of conftest.py files."),
- group.addoption('--debug',
- action="store_true", dest="debug", default=False,
- help="store internal tracing debug information in 'pytestdebug.log'.")
-
-
-def pytest_cmdline_parse(__multicall__):
- config = __multicall__.execute()
- if config.option.debug:
- path = os.path.abspath("pytestdebug.log")
- f = open(path, 'w')
- config._debugfile = f
- f.write("versions pytest-%s, py-%s, python-%s\ncwd=%s\nargs=%s\n\n" %(
- pytest.__version__, py.__version__, ".".join(map(str, sys.version_info)),
- os.getcwd(), config._origargs))
- config.trace.root.setwriter(f.write)
- sys.stderr.write("writing pytestdebug information to %s\n" % path)
- return config
-
- at pytest.mark.trylast
-def pytest_unconfigure(config):
- if hasattr(config, '_debugfile'):
- config._debugfile.close()
- sys.stderr.write("wrote pytestdebug information to %s\n" %
- config._debugfile.name)
- config.trace.root.setwriter(None)
-
-
-def pytest_cmdline_main(config):
- if config.option.version:
- p = py.path.local(pytest.__file__)
- sys.stderr.write("This is py.test version %s, imported from %s\n" %
- (pytest.__version__, p))
- plugininfo = getpluginversioninfo(config)
- if plugininfo:
- for line in plugininfo:
- sys.stderr.write(line + "\n")
- return 0
- elif config.option.help:
- config.pluginmanager.do_configure(config)
- showhelp(config)
- config.pluginmanager.do_unconfigure(config)
- return 0
-
-def showhelp(config):
- tw = py.io.TerminalWriter()
- tw.write(config._parser.optparser.format_help())
- tw.line()
- tw.line()
- #tw.sep( "=", "config file settings")
- tw.line("[pytest] ini-options in the next "
- "pytest.ini|tox.ini|setup.cfg file:")
- tw.line()
-
- for name in config._parser._ininames:
- help, type, default = config._parser._inidict[name]
- if type is None:
- type = "string"
- spec = "%s (%s)" % (name, type)
- line = " %-24s %s" %(spec, help)
- tw.line(line[:tw.fullwidth])
-
- tw.line() ; tw.line()
- #tw.sep("=")
- return
-
- tw.line("conftest.py options:")
- tw.line()
- conftestitems = sorted(config._parser._conftestdict.items())
- for name, help in conftest_options + conftestitems:
- line = " %-15s %s" %(name, help)
- tw.line(line[:tw.fullwidth])
- tw.line()
- #tw.sep( "=")
-
-conftest_options = [
- ('pytest_plugins', 'list of plugin names to load'),
-]
-
-def getpluginversioninfo(config):
- lines = []
- plugininfo = config.pluginmanager._plugin_distinfo
- if plugininfo:
- lines.append("setuptools registered plugins:")
- for dist, plugin in plugininfo:
- loc = getattr(plugin, '__file__', repr(plugin))
- content = "%s-%s at %s" % (dist.project_name, dist.version, loc)
- lines.append(" " + content)
- return lines
-
-def pytest_report_header(config):
- lines = []
- if config.option.debug or config.option.traceconfig:
- lines.append("using: pytest-%s pylib-%s" %
- (pytest.__version__,py.__version__))
-
- verinfo = getpluginversioninfo(config)
- if verinfo:
- lines.extend(verinfo)
-
- if config.option.traceconfig:
- lines.append("active plugins:")
- plugins = []
- items = config.pluginmanager._name2plugin.items()
- for name, plugin in items:
- if hasattr(plugin, '__file__'):
- r = plugin.__file__
- else:
- r = repr(plugin)
- lines.append(" %-20s: %s" %(name, r))
- return lines
-
-
-# =====================================================
-# validate plugin syntax and hooks
-# =====================================================
-
-def pytest_plugin_registered(manager, plugin):
- methods = collectattr(plugin)
- hooks = {}
- for hookspec in manager.hook._hookspecs:
- hooks.update(collectattr(hookspec))
-
- stringio = py.io.TextIO()
- def Print(*args):
- if args:
- stringio.write(" ".join(map(str, args)))
- stringio.write("\n")
-
- fail = False
- while methods:
- name, method = methods.popitem()
- #print "checking", name
- if isgenerichook(name):
- continue
- if name not in hooks:
- if not getattr(method, 'optionalhook', False):
- Print("found unknown hook:", name)
- fail = True
- else:
- #print "checking", method
- method_args = list(varnames(method))
- if '__multicall__' in method_args:
- method_args.remove('__multicall__')
- hook = hooks[name]
- hookargs = varnames(hook)
- for arg in method_args:
- if arg not in hookargs:
- Print("argument %r not available" %(arg, ))
- Print("actual definition: %s" %(formatdef(method)))
- Print("available hook arguments: %s" %
- ", ".join(hookargs))
- fail = True
- break
- #if not fail:
- # print "matching hook:", formatdef(method)
- if fail:
- name = getattr(plugin, '__name__', plugin)
- raise PluginValidationError("%s:\n%s" % (name, stringio.getvalue()))
-
-class PluginValidationError(Exception):
- """ plugin failed validation. """
-
-def isgenerichook(name):
- return name == "pytest_plugins" or \
- name.startswith("pytest_funcarg__")
-
-def collectattr(obj):
- methods = {}
- for apiname in dir(obj):
- if apiname.startswith("pytest_"):
- methods[apiname] = getattr(obj, apiname)
- return methods
-
-def formatdef(func):
- return "%s%s" % (
- func.__name__,
- inspect.formatargspec(*inspect.getargspec(func))
- )
-
diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py
deleted file mode 100644
--- a/_pytest/hookspec.py
+++ /dev/null
@@ -1,228 +0,0 @@
-""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """
-
-# -------------------------------------------------------------------------
-# Initialization
-# -------------------------------------------------------------------------
-
-def pytest_addhooks(pluginmanager):
- """called at plugin load time to allow adding new hooks via a call to
- pluginmanager.registerhooks(module)."""
-
-
-def pytest_namespace():
- """return dict of name->object to be made globally available in
- the py.test/pytest namespace. This hook is called before command
- line options are parsed.
- """
-
-def pytest_cmdline_parse(pluginmanager, args):
- """return initialized config object, parsing the specified args. """
-pytest_cmdline_parse.firstresult = True
-
-def pytest_cmdline_preparse(config, args):
- """modify command line arguments before option parsing. """
-
-def pytest_addoption(parser):
- """add optparse-style options and ini-style config values via calls
- to ``parser.addoption`` and ``parser.addini(...)``.
- """
-
-def pytest_cmdline_main(config):
- """ called for performing the main command line action. The default
- implementation will invoke the configure hooks and runtest_mainloop. """
-pytest_cmdline_main.firstresult = True
-
-def pytest_configure(config):
- """ called after command line options have been parsed.
- and all plugins and initial conftest files been loaded.
- """
-
-def pytest_unconfigure(config):
- """ called before test process is exited. """
-
-def pytest_runtestloop(session):
- """ called for performing the main runtest loop
- (after collection finished). """
-pytest_runtestloop.firstresult = True
-
-# -------------------------------------------------------------------------
-# collection hooks
-# -------------------------------------------------------------------------
-
-def pytest_collection(session):
- """ perform the collection protocol for the given session. """
-pytest_collection.firstresult = True
-
-def pytest_collection_modifyitems(session, config, items):
- """ called after collection has been performed, may filter or re-order
- the items in-place."""
-
-def pytest_collection_finish(session):
- """ called after collection has been performed and modified. """
-
-def pytest_ignore_collect(path, config):
- """ return True to prevent considering this path for collection.
- This hook is consulted for all files and directories prior to calling
- more specific hooks.
- """
-pytest_ignore_collect.firstresult = True
-
-def pytest_collect_directory(path, parent):
- """ called before traversing a directory for collection files. """
-pytest_collect_directory.firstresult = True
-
-def pytest_collect_file(path, parent):
- """ return collection Node or None for the given path. Any new node
- needs to have the specified ``parent`` as a parent."""
-
-# logging hooks for collection
-def pytest_collectstart(collector):
- """ collector starts collecting. """
-
-def pytest_itemcollected(item):
- """ we just collected a test item. """
-
-def pytest_collectreport(report):
- """ collector finished collecting. """
-
-def pytest_deselected(items):
- """ called for test items deselected by keyword. """
-
-def pytest_make_collect_report(collector):
- """ perform ``collector.collect()`` and return a CollectReport. """
-pytest_make_collect_report.firstresult = True
-
-# -------------------------------------------------------------------------
-# Python test function related hooks
-# -------------------------------------------------------------------------
-
-def pytest_pycollect_makemodule(path, parent):
- """ return a Module collector or None for the given path.
- This hook will be called for each matching test module path.
- The pytest_collect_file hook needs to be used if you want to
- create test modules for files that do not match as a test module.
- """
-pytest_pycollect_makemodule.firstresult = True
-
-def pytest_pycollect_makeitem(collector, name, obj):
- """ return custom item/collector for a python object in a module, or None. """
-pytest_pycollect_makeitem.firstresult = True
-
-def pytest_pyfunc_call(pyfuncitem):
- """ call underlying test function. """
-pytest_pyfunc_call.firstresult = True
-
-def pytest_generate_tests(metafunc):
- """ generate (multiple) parametrized calls to a test function."""
-
-# -------------------------------------------------------------------------
-# generic runtest related hooks
-# -------------------------------------------------------------------------
-def pytest_itemstart(item, node=None):
- """ (deprecated, use pytest_runtest_logstart). """
-
-def pytest_runtest_protocol(item, nextitem):
- """ implements the runtest_setup/call/teardown protocol for
- the given test item, including capturing exceptions and calling
- reporting hooks.
-
- :arg item: test item for which the runtest protocol is performed.
-
- :arg nexitem: the scheduled-to-be-next test item (or None if this
- is the end my friend). This argument is passed on to
- :py:func:`pytest_runtest_teardown`.
-
- :return boolean: True if no further hook implementations should be invoked.
- """
-pytest_runtest_protocol.firstresult = True
-
-def pytest_runtest_logstart(nodeid, location):
- """ signal the start of running a single test item. """
-
-def pytest_runtest_setup(item):
- """ called before ``pytest_runtest_call(item)``. """
-
-def pytest_runtest_call(item):
- """ called to execute the test ``item``. """
-
-def pytest_runtest_teardown(item, nextitem):
- """ called after ``pytest_runtest_call``.
-
- :arg nexitem: the scheduled-to-be-next test item (None if no further
- test item is scheduled). This argument can be used to
- perform exact teardowns, i.e. calling just enough finalizers
- so that nextitem only needs to call setup-functions.
- """
-
-def pytest_runtest_makereport(item, call):
- """ return a :py:class:`_pytest.runner.TestReport` object
- for the given :py:class:`pytest.Item` and
- :py:class:`_pytest.runner.CallInfo`.
- """
-pytest_runtest_makereport.firstresult = True
-
-def pytest_runtest_logreport(report):
- """ process a test setup/call/teardown report relating to
- the respective phase of executing a test. """
-
-# -------------------------------------------------------------------------
-# test session related hooks
-# -------------------------------------------------------------------------
-
-def pytest_sessionstart(session):
- """ before session.main() is called. """
-
-def pytest_sessionfinish(session, exitstatus):
- """ whole test run finishes. """
-
-
-# -------------------------------------------------------------------------
-# hooks for customising the assert methods
-# -------------------------------------------------------------------------
-
-def pytest_assertrepr_compare(config, op, left, right):
- """return explanation for comparisons in failing assert expressions.
-
- Return None for no custom explanation, otherwise return a list
- of strings. The strings will be joined by newlines but any newlines
- *in* a string will be escaped. Note that all but the first line will
- be indented sligthly, the intention is for the first line to be a summary.
- """
-
-# -------------------------------------------------------------------------
-# hooks for influencing reporting (invoked from _pytest_terminal)
-# -------------------------------------------------------------------------
-
-def pytest_report_header(config):
- """ return a string to be displayed as header info for terminal reporting."""
-
-def pytest_report_teststatus(report):
- """ return result-category, shortletter and verbose word for reporting."""
-pytest_report_teststatus.firstresult = True
-
-def pytest_terminal_summary(terminalreporter):
- """ add additional section in terminal summary reporting. """
-
-# -------------------------------------------------------------------------
-# doctest hooks
-# -------------------------------------------------------------------------
-
-def pytest_doctest_prepare_content(content):
- """ return processed content for a given doctest"""
-pytest_doctest_prepare_content.firstresult = True
-
-# -------------------------------------------------------------------------
-# error handling and internal debugging hooks
-# -------------------------------------------------------------------------
-
-def pytest_plugin_registered(plugin, manager):
- """ a new py lib plugin got registered. """
-
-def pytest_plugin_unregistered(plugin):
- """ a py lib plugin got unregistered. """
-
-def pytest_internalerror(excrepr):
- """ called for internal errors. """
-
-def pytest_keyboard_interrupt(excinfo):
- """ called for keyboard interrupt. """
diff --git a/_pytest/junitxml.py b/_pytest/junitxml.py
deleted file mode 100644
--- a/_pytest/junitxml.py
+++ /dev/null
@@ -1,220 +0,0 @@
-""" report test results in JUnit-XML format, for use with Hudson and build integration servers.
-
-Based on initial code from Ross Lawley.
-"""
-
-import py
-import os
-import re
-import sys
-import time
-
-
-# Python 2.X and 3.X compatibility
-try:
- unichr(65)
-except NameError:
- unichr = chr
-try:
- unicode('A')
-except NameError:
- unicode = str
-try:
- long(1)
-except NameError:
- long = int
-
-
-class Junit(py.xml.Namespace):
- pass
-
-
-# We need to get the subset of the invalid unicode ranges according to
-# XML 1.0 which are valid in this python build. Hence we calculate
-# this dynamically instead of hardcoding it. The spec range of valid
-# chars is: Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
-# | [#x10000-#x10FFFF]
-_legal_chars = (0x09, 0x0A, 0x0d)
-_legal_ranges = (
- (0x20, 0xD7FF),
- (0xE000, 0xFFFD),
- (0x10000, 0x10FFFF),
-)
-_legal_xml_re = [unicode("%s-%s") % (unichr(low), unichr(high))
- for (low, high) in _legal_ranges
- if low < sys.maxunicode]
-_legal_xml_re = [unichr(x) for x in _legal_chars] + _legal_xml_re
-illegal_xml_re = re.compile(unicode('[^%s]') %
- unicode('').join(_legal_xml_re))
-del _legal_chars
-del _legal_ranges
-del _legal_xml_re
-
-def bin_xml_escape(arg):
- def repl(matchobj):
- i = ord(matchobj.group())
- if i <= 0xFF:
- return unicode('#x%02X') % i
- else:
- return unicode('#x%04X') % i
- return illegal_xml_re.sub(repl, py.xml.escape(arg))
-
-def pytest_addoption(parser):
- group = parser.getgroup("terminal reporting")
- group.addoption('--junitxml', action="store", dest="xmlpath",
- metavar="path", default=None,
- help="create junit-xml style report file at given path.")
- group.addoption('--junitprefix', action="store", dest="junitprefix",
- metavar="str", default=None,
- help="prepend prefix to classnames in junit-xml output")
-
-def pytest_configure(config):
- xmlpath = config.option.xmlpath
- if xmlpath:
- config._xml = LogXML(xmlpath, config.option.junitprefix)
- config.pluginmanager.register(config._xml)
-
-def pytest_unconfigure(config):
- xml = getattr(config, '_xml', None)
- if xml:
- del config._xml
- config.pluginmanager.unregister(xml)
-
-
-class LogXML(object):
- def __init__(self, logfile, prefix):
- logfile = os.path.expanduser(os.path.expandvars(logfile))
- self.logfile = os.path.normpath(logfile)
- self.prefix = prefix
- self.tests = []
- self.passed = self.skipped = 0
- self.failed = self.errors = 0
-
- def _opentestcase(self, report):
- names = report.nodeid.split("::")
- names[0] = names[0].replace("/", '.')
- names = [x.replace(".py", "") for x in names if x != "()"]
- classnames = names[:-1]
- if self.prefix:
- classnames.insert(0, self.prefix)
- self.tests.append(Junit.testcase(
- classname=".".join(classnames),
- name=names[-1],
- time=getattr(report, 'duration', 0)
- ))
-
- def append(self, obj):
- self.tests[-1].append(obj)
-
- def append_pass(self, report):
- self.passed += 1
-
- def append_failure(self, report):
- #msg = str(report.longrepr.reprtraceback.extraline)
- if "xfail" in report.keywords:
- self.append(
- Junit.skipped(message="xfail-marked test passes unexpectedly"))
- self.skipped += 1
- else:
- sec = dict(report.sections)
- fail = Junit.failure(message="test failure")
- fail.append(str(report.longrepr))
- self.append(fail)
- for name in ('out', 'err'):
- content = sec.get("Captured std%s" % name)
- if content:
- tag = getattr(Junit, 'system-'+name)
- self.append(tag(bin_xml_escape(content)))
- self.failed += 1
-
- def append_collect_failure(self, report):
- #msg = str(report.longrepr.reprtraceback.extraline)
- self.append(Junit.failure(str(report.longrepr),
- message="collection failure"))
- self.errors += 1
-
- def append_collect_skipped(self, report):
- #msg = str(report.longrepr.reprtraceback.extraline)
- self.append(Junit.skipped(str(report.longrepr),
- message="collection skipped"))
- self.skipped += 1
-
- def append_error(self, report):
- self.append(Junit.error(str(report.longrepr),
- message="test setup failure"))
- self.errors += 1
-
- def append_skipped(self, report):
- if "xfail" in report.keywords:
- self.append(Junit.skipped(str(report.keywords['xfail']),
- message="expected test failure"))
- else:
- filename, lineno, skipreason = report.longrepr
- if skipreason.startswith("Skipped: "):
- skipreason = skipreason[9:]
- self.append(
- Junit.skipped("%s:%s: %s" % report.longrepr,
- type="pytest.skip",
- message=skipreason
- ))
- self.skipped += 1
-
- def pytest_runtest_logreport(self, report):
- if report.passed:
- if report.when == "call": # ignore setup/teardown
- self._opentestcase(report)
- self.append_pass(report)
- elif report.failed:
- self._opentestcase(report)
- if report.when != "call":
- self.append_error(report)
- else:
- self.append_failure(report)
- elif report.skipped:
- self._opentestcase(report)
- self.append_skipped(report)
-
- def pytest_collectreport(self, report):
- if not report.passed:
- self._opentestcase(report)
- if report.failed:
- self.append_collect_failure(report)
- else:
- self.append_collect_skipped(report)
-
- def pytest_internalerror(self, excrepr):
- self.errors += 1
- data = py.xml.escape(excrepr)
- self.tests.append(
- Junit.testcase(
- Junit.error(data, message="internal error"),
- classname="pytest",
- name="internal"))
-
- def pytest_sessionstart(self, session):
- self.suite_start_time = time.time()
-
- def pytest_sessionfinish(self, session, exitstatus, __multicall__):
- if py.std.sys.version_info[0] < 3:
- logfile = py.std.codecs.open(self.logfile, 'w', encoding='utf-8')
- else:
- logfile = open(self.logfile, 'w', encoding='utf-8')
-
- suite_stop_time = time.time()
- suite_time_delta = suite_stop_time - self.suite_start_time
- numtests = self.passed + self.failed
-
- logfile.write('<?xml version="1.0" encoding="utf-8"?>')
- logfile.write(Junit.testsuite(
- self.tests,
- name="",
- errors=self.errors,
- failures=self.failed,
- skips=self.skipped,
- tests=numtests,
- time="%.3f" % suite_time_delta,
- ).unicode(indent=0))
- logfile.close()
-
- def pytest_terminal_summary(self, terminalreporter):
- terminalreporter.write_sep("-", "generated xml file: %s" % (self.logfile))
diff --git a/_pytest/main.py b/_pytest/main.py
deleted file mode 100644
--- a/_pytest/main.py
+++ /dev/null
@@ -1,576 +0,0 @@
-""" core implementation of testing process: init, session, runtest loop. """
-
-import py
-import pytest, _pytest
-import os, sys, imp
-tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
-
-# exitcodes for the command line
-EXIT_OK = 0
-EXIT_TESTSFAILED = 1
-EXIT_INTERRUPTED = 2
-EXIT_INTERNALERROR = 3
-
-name_re = py.std.re.compile("^[a-zA-Z_]\w*$")
-
-def pytest_addoption(parser):
- parser.addini("norecursedirs", "directory patterns to avoid for recursion",
- type="args", default=('.*', 'CVS', '_darcs', '{arch}'))
- #parser.addini("dirpatterns",
- # "patterns specifying possible locations of test files",
- # type="linelist", default=["**/test_*.txt",
- # "**/test_*.py", "**/*_test.py"]
- #)
- group = parser.getgroup("general", "running and selection options")
- group._addoption('-x', '--exitfirst', action="store_true", default=False,
- dest="exitfirst",
- help="exit instantly on first error or failed test."),
- group._addoption('--maxfail', metavar="num",
- action="store", type="int", dest="maxfail", default=0,
- help="exit after first num failures or errors.")
-
- group._addoption('--strict', action="store_true",
- help="run pytest in strict mode, warnings become errors.")
-
- group = parser.getgroup("collect", "collection")
- group.addoption('--collectonly',
- action="store_true", dest="collectonly",
- help="only collect tests, don't execute them."),
- group.addoption('--pyargs', action="store_true",
- help="try to interpret all arguments as python packages.")
- group.addoption("--ignore", action="append", metavar="path",
- help="ignore path during collection (multi-allowed).")
- group.addoption('--confcutdir', dest="confcutdir", default=None,
- metavar="dir",
- help="only load conftest.py's relative to specified dir.")
-
- group = parser.getgroup("debugconfig",
- "test session debugging and configuration")
- group.addoption('--basetemp', dest="basetemp", default=None, metavar="dir",
- help="base temporary directory for this test run.")
-
-
-def pytest_namespace():
- collect = dict(Item=Item, Collector=Collector, File=File, Session=Session)
- return dict(collect=collect)
-
-def pytest_configure(config):
- py.test.config = config # compatibiltiy
- if config.option.exitfirst:
- config.option.maxfail = 1
-
-def wrap_session(config, doit):
- """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
- except KeyboardInterrupt:
- excinfo = py.code.ExceptionInfo()
- config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
- session.exitstatus = EXIT_INTERRUPTED
- except:
- excinfo = py.code.ExceptionInfo()
- config.pluginmanager.notify_exception(excinfo, config.option)
- session.exitstatus = EXIT_INTERNALERROR
- if excinfo.errisinstance(SystemExit):
- sys.stderr.write("mainloop: caught Spurious SystemExit!\n")
- if initstate >= 2:
- config.hook.pytest_sessionfinish(session=session,
- exitstatus=session.exitstatus or (session._testsfailed and 1))
- if not session.exitstatus and session._testsfailed:
- session.exitstatus = EXIT_TESTSFAILED
- if initstate >= 1:
- config.pluginmanager.do_unconfigure(config)
- return session.exitstatus
-
-def pytest_cmdline_main(config):
- return wrap_session(config, _main)
-
-def _main(config, session):
- """ default command line protocol for initialization, session,
- running tests and reporting. """
- config.hook.pytest_collection(session=session)
- config.hook.pytest_runtestloop(session=session)
-
-def pytest_collection(session):
- return session.perform_collect()
-
-def pytest_runtestloop(session):
- if session.config.option.collectonly:
- return True
- for i, item in enumerate(session.items):
- try:
- nextitem = session.items[i+1]
- except IndexError:
- nextitem = None
- item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
- if session.shouldstop:
- raise session.Interrupted(session.shouldstop)
- return True
-
-def pytest_ignore_collect(path, config):
- p = path.dirpath()
- ignore_paths = config._getconftest_pathlist("collect_ignore", path=p)
- ignore_paths = ignore_paths or []
- excludeopt = config.getvalue("ignore")
- if excludeopt:
- ignore_paths.extend([py.path.local(x) for x in excludeopt])
- return path in ignore_paths
-
-class HookProxy:
- def __init__(self, fspath, config):
- self.fspath = fspath
- self.config = config
- def __getattr__(self, name):
- hookmethod = getattr(self.config.hook, name)
- def call_matching_hooks(**kwargs):
- plugins = self.config._getmatchingplugins(self.fspath)
- return hookmethod.pcall(plugins, **kwargs)
- return call_matching_hooks
-
-def compatproperty(name):
- def fget(self):
- return getattr(pytest, name)
- return property(fget, None, None,
- "deprecated attribute %r, use pytest.%s" % (name,name))
-
-class Node(object):
- """ base class for all Nodes in the collection tree.
- Collector subclasses have children, Items are terminal nodes."""
-
- def __init__(self, name, parent=None, config=None, session=None):
- #: a unique name with the scope of the parent
- self.name = name
-
- #: the parent collector node.
- self.parent = parent
-
- #: the test config object
- self.config = config or parent.config
-
- #: the collection this node is part of
- self.session = session or parent.session
-
- #: filesystem path where this node was collected from
- self.fspath = getattr(parent, 'fspath', None)
- self.ihook = self.session.gethookproxy(self.fspath)
- self.keywords = {self.name: True}
-
- Module = compatproperty("Module")
- Class = compatproperty("Class")
- Instance = compatproperty("Instance")
- Function = compatproperty("Function")
- File = compatproperty("File")
- Item = compatproperty("Item")
-
- def _getcustomclass(self, name):
- cls = getattr(self, name)
- if cls != getattr(pytest, name):
- py.log._apiwarn("2.0", "use of node.%s is deprecated, "
- "use pytest_pycollect_makeitem(...) to create custom "
- "collection nodes" % name)
- return cls
-
- def __repr__(self):
- return "<%s %r>" %(self.__class__.__name__, getattr(self, 'name', None))
-
- # methods for ordering nodes
- @property
- def nodeid(self):
- try:
- return self._nodeid
- except AttributeError:
- self._nodeid = x = self._makeid()
- return x
-
- def _makeid(self):
- return self.parent.nodeid + "::" + self.name
-
- def __eq__(self, other):
- if not isinstance(other, Node):
- return False
- return self.__class__ == other.__class__ and \
- self.name == other.name and self.parent == other.parent
-
- def __ne__(self, other):
- return not self == other
-
- def __hash__(self):
- return hash((self.name, self.parent))
-
- def setup(self):
- pass
-
- def teardown(self):
- pass
-
- def _memoizedcall(self, attrname, function):
- exattrname = "_ex_" + attrname
- failure = getattr(self, exattrname, None)
- if failure is not None:
- py.builtin._reraise(failure[0], failure[1], failure[2])
- if hasattr(self, attrname):
- return getattr(self, attrname)
- try:
- res = function()
- except py.builtin._sysex:
- raise
- except:
- failure = py.std.sys.exc_info()
- setattr(self, exattrname, failure)
- raise
- setattr(self, attrname, res)
- return res
-
- def listchain(self):
- """ return list of all parent collectors up to self,
- starting from root of collection tree. """
- chain = []
- item = self
- while item is not None:
- chain.append(item)
- item = item.parent
- chain.reverse()
- return chain
-
- def listnames(self):
- return [x.name for x in self.listchain()]
-
- def getplugins(self):
- return self.config._getmatchingplugins(self.fspath)
-
- def getparent(self, cls):
- current = self
- while current and not isinstance(current, cls):
- current = current.parent
- return current
-
- def _prunetraceback(self, excinfo):
- pass
-
- def _repr_failure_py(self, excinfo, style=None):
- if self.config.option.fulltrace:
- style="long"
- else:
- self._prunetraceback(excinfo)
- # XXX should excinfo.getrepr record all data and toterminal()
- # process it?
- if style is None:
- if self.config.option.tbstyle == "short":
- style = "short"
- else:
- style = "long"
- return excinfo.getrepr(funcargs=True,
- showlocals=self.config.option.showlocals,
- style=style)
-
- repr_failure = _repr_failure_py
-
-class Collector(Node):
- """ Collector instances create children through collect()
- and thus iteratively build a tree.
- """
- class CollectError(Exception):
- """ an error during collection, contains a custom message. """
-
- def collect(self):
- """ returns a list of children (items and collectors)
- for this collection node.
- """
- raise NotImplementedError("abstract")
-
- def repr_failure(self, excinfo):
- """ represent a collection failure. """
- if excinfo.errisinstance(self.CollectError):
- exc = excinfo.value
- return str(exc.args[0])
- return self._repr_failure_py(excinfo, style="short")
-
- def _memocollect(self):
- """ internal helper method to cache results of calling collect(). """
- return self._memoizedcall('_collected', lambda: list(self.collect()))
-
- def _prunetraceback(self, excinfo):
- if hasattr(self, 'fspath'):
- path = self.fspath
- traceback = excinfo.traceback
- ntraceback = traceback.cut(path=self.fspath)
- if ntraceback == traceback:
- ntraceback = ntraceback.cut(excludepath=tracebackcutdir)
- excinfo.traceback = ntraceback.filter()
-
-class FSCollector(Collector):
- def __init__(self, fspath, parent=None, config=None, session=None):
- fspath = py.path.local(fspath) # xxx only for test_resultlog.py?
- name = fspath.basename
- if parent is not None:
- rel = fspath.relto(parent.fspath)
- if rel:
- name = rel
- name = name.replace(os.sep, "/")
- super(FSCollector, self).__init__(name, parent, config, session)
- self.fspath = fspath
-
- def _makeid(self):
- if self == self.session:
- return "."
- relpath = self.session.fspath.bestrelpath(self.fspath)
- if os.sep != "/":
- relpath = relpath.replace(os.sep, "/")
- return relpath
-
-class File(FSCollector):
- """ base class for collecting tests from a file. """
-
-class Item(Node):
- """ a basic test invocation item. Note that for a single function
- there might be multiple test invocation items.
- """
- nextitem = None
-
- def reportinfo(self):
- return self.fspath, None, ""
-
- @property
- def location(self):
- try:
- return self._location
- except AttributeError:
- location = self.reportinfo()
- # bestrelpath is a quite slow function
- cache = self.config.__dict__.setdefault("_bestrelpathcache", {})
- try:
- fspath = cache[location[0]]
- except KeyError:
- fspath = self.session.fspath.bestrelpath(location[0])
- cache[location[0]] = fspath
- location = (fspath, location[1], str(location[2]))
- self._location = location
- return location
-
-class NoMatch(Exception):
- """ raised if matching cannot locate a matching names. """
-
-class Session(FSCollector):
- class Interrupted(KeyboardInterrupt):
- """ signals an interrupted test run. """
- __module__ = 'builtins' # for py3
-
- def __init__(self, config):
- super(Session, self).__init__(py.path.local(), parent=None,
- config=config, session=self)
- assert self.config.pluginmanager.register(self, name="session", prepend=True)
- self._testsfailed = 0
- self.shouldstop = False
- self.trace = config.trace.root.get("collection")
- self._norecursepatterns = config.getini("norecursedirs")
-
- def pytest_collectstart(self):
- if self.shouldstop:
- raise self.Interrupted(self.shouldstop)
-
- def pytest_runtest_logreport(self, report):
- if report.failed and 'xfail' not in getattr(report, 'keywords', []):
- self._testsfailed += 1
- maxfail = self.config.getvalue("maxfail")
- if maxfail and self._testsfailed >= maxfail:
- self.shouldstop = "stopping after %d failures" % (
- self._testsfailed)
- pytest_collectreport = pytest_runtest_logreport
-
- def isinitpath(self, path):
- return path in self._initialpaths
-
- def gethookproxy(self, fspath):
- return HookProxy(fspath, self.config)
-
- def perform_collect(self, args=None, genitems=True):
- hook = self.config.hook
- try:
- items = self._perform_collect(args, genitems)
- hook.pytest_collection_modifyitems(session=self,
- config=self.config, items=items)
- finally:
- hook.pytest_collection_finish(session=self)
- return items
-
- def _perform_collect(self, args, genitems):
- if args is None:
- args = self.config.args
- self.trace("perform_collect", self, args)
- self.trace.root.indent += 1
- self._notfound = []
- self._initialpaths = set()
- self._initialparts = []
- self.items = items = []
- for arg in args:
- parts = self._parsearg(arg)
- self._initialparts.append(parts)
- self._initialpaths.add(parts[0])
- self.ihook.pytest_collectstart(collector=self)
- rep = self.ihook.pytest_make_collect_report(collector=self)
- self.ihook.pytest_collectreport(report=rep)
- self.trace.root.indent -= 1
- if self._notfound:
- for arg, exc in self._notfound:
- line = "(no name %r in any of %r)" % (arg, exc.args[0])
- raise pytest.UsageError("not found: %s\n%s" %(arg, line))
- if not genitems:
- return rep.result
- else:
- if rep.passed:
- for node in rep.result:
- self.items.extend(self.genitems(node))
- return items
-
- def collect(self):
- for parts in self._initialparts:
- arg = "::".join(map(str, parts))
- self.trace("processing argument", arg)
- self.trace.root.indent += 1
- try:
- for x in self._collect(arg):
- yield x
- except NoMatch:
- # we are inside a make_report hook so
- # we cannot directly pass through the exception
- self._notfound.append((arg, sys.exc_info()[1]))
- self.trace.root.indent -= 1
- break
- self.trace.root.indent -= 1
-
- def _collect(self, arg):
- names = self._parsearg(arg)
- path = names.pop(0)
- if path.check(dir=1):
- assert not names, "invalid arg %r" %(arg,)
- for path in path.visit(fil=lambda x: x.check(file=1),
- rec=self._recurse, bf=True, sort=True):
- for x in self._collectfile(path):
- yield x
- else:
- assert path.check(file=1)
- for x in self.matchnodes(self._collectfile(path), names):
- yield x
-
- def _collectfile(self, path):
- ihook = self.gethookproxy(path)
- if not self.isinitpath(path):
- if ihook.pytest_ignore_collect(path=path, config=self.config):
- return ()
- return ihook.pytest_collect_file(path=path, parent=self)
-
- def _recurse(self, path):
- ihook = self.gethookproxy(path.dirpath())
- if ihook.pytest_ignore_collect(path=path, config=self.config):
- return
- for pat in self._norecursepatterns:
- if path.check(fnmatch=pat):
- return False
- ihook = self.gethookproxy(path)
- ihook.pytest_collect_directory(path=path, parent=self)
- return True
-
- def _tryconvertpyarg(self, x):
- mod = None
- path = [os.path.abspath('.')] + sys.path
- for name in x.split('.'):
- # ignore anything that's not a proper name here
- # else something like --pyargs will mess up '.'
- # since imp.find_module will actually sometimes work for it
- # but it's supposed to be considered a filesystem path
- # not a package
- if name_re.match(name) is None:
- return x
- try:
- fd, mod, type_ = imp.find_module(name, path)
- except ImportError:
- return x
- else:
- if fd is not None:
- fd.close()
-
- if type_[2] != imp.PKG_DIRECTORY:
- path = [os.path.dirname(mod)]
- else:
- path = [mod]
- return mod
-
- def _parsearg(self, arg):
- """ return (fspath, names) tuple after checking the file exists. """
- arg = str(arg)
- if self.config.option.pyargs:
- arg = self._tryconvertpyarg(arg)
- parts = str(arg).split("::")
- relpath = parts[0].replace("/", os.sep)
- path = self.fspath.join(relpath, abs=True)
- if not path.check():
- if self.config.option.pyargs:
- msg = "file or package not found: "
- else:
- msg = "file not found: "
- raise pytest.UsageError(msg + arg)
- parts[0] = path
- return parts
-
- def matchnodes(self, matching, names):
- self.trace("matchnodes", matching, names)
- self.trace.root.indent += 1
- nodes = self._matchnodes(matching, names)
- num = len(nodes)
- self.trace("matchnodes finished -> ", num, "nodes")
- self.trace.root.indent -= 1
- if num == 0:
- raise NoMatch(matching, names[:1])
- return nodes
-
- def _matchnodes(self, matching, names):
- if not matching or not names:
- return matching
- name = names[0]
- assert name
- nextnames = names[1:]
- resultnodes = []
- for node in matching:
- if isinstance(node, pytest.Item):
- if not names:
- resultnodes.append(node)
- continue
- assert isinstance(node, pytest.Collector)
- node.ihook.pytest_collectstart(collector=node)
- rep = node.ihook.pytest_make_collect_report(collector=node)
- if rep.passed:
- has_matched = False
- for x in rep.result:
- if x.name == name:
- resultnodes.extend(self.matchnodes([x], nextnames))
- has_matched = True
- # XXX accept IDs that don't have "()" for class instances
- if not has_matched and len(rep.result) == 1 and x.name == "()":
- nextnames.insert(0, name)
- resultnodes.extend(self.matchnodes([x], nextnames))
- node.ihook.pytest_collectreport(report=rep)
- return resultnodes
-
- def genitems(self, node):
- self.trace("genitems", node)
- if isinstance(node, pytest.Item):
- node.ihook.pytest_itemcollected(item=node)
- yield node
- else:
- assert isinstance(node, pytest.Collector)
- node.ihook.pytest_collectstart(collector=node)
- rep = node.ihook.pytest_make_collect_report(collector=node)
- if rep.passed:
- for subnode in rep.result:
- for x in self.genitems(subnode):
- yield x
- node.ihook.pytest_collectreport(report=rep)
diff --git a/_pytest/mark.py b/_pytest/mark.py
deleted file mode 100644
--- a/_pytest/mark.py
+++ /dev/null
@@ -1,226 +0,0 @@
-""" generic mechanism for marking and selecting python functions. """
-import pytest, py
-
-def pytest_namespace():
- return {'mark': MarkGenerator()}
-
-def pytest_addoption(parser):
- group = parser.getgroup("general")
- group._addoption('-k',
- action="store", dest="keyword", default='', metavar="KEYWORDEXPR",
- help="only run tests which match given keyword expression. "
- "An expression consists of space-separated terms. "
- "Each term must match. Precede a term with '-' to negate. "
- "Terminate expression with ':' to make the first match match "
- "all subsequent tests (usually file-order). ")
-
- group._addoption("-m",
- action="store", dest="markexpr", default="", metavar="MARKEXPR",
- help="only run tests matching given mark expression. "
- "example: -m 'mark1 and not mark2'."
- )
-
- group.addoption("--markers", action="store_true", help=
- "show markers (builtin, plugin and per-project ones).")
-
- parser.addini("markers", "markers for test functions", 'linelist')
-
-def pytest_cmdline_main(config):
- if config.option.markers:
- config.pluginmanager.do_configure(config)
- tw = py.io.TerminalWriter()
- for line in config.getini("markers"):
- name, rest = line.split(":", 1)
- tw.write("@pytest.mark.%s:" % name, bold=True)
- tw.line(rest)
- tw.line()
- config.pluginmanager.do_unconfigure(config)
- return 0
-pytest_cmdline_main.tryfirst = True
-
-def pytest_collection_modifyitems(items, config):
- keywordexpr = config.option.keyword
- matchexpr = config.option.markexpr
- if not keywordexpr and not matchexpr:
- return
- selectuntil = False
- if keywordexpr[-1:] == ":":
- selectuntil = True
- keywordexpr = keywordexpr[:-1]
-
- remaining = []
- deselected = []
- for colitem in items:
- if keywordexpr and skipbykeyword(colitem, keywordexpr):
- deselected.append(colitem)
- else:
- if selectuntil:
- keywordexpr = None
- if matchexpr:
- if not matchmark(colitem, matchexpr):
- deselected.append(colitem)
- continue
- remaining.append(colitem)
-
- if deselected:
- config.hook.pytest_deselected(items=deselected)
- items[:] = remaining
-
-class BoolDict:
- def __init__(self, mydict):
- self._mydict = mydict
- def __getitem__(self, name):
- return name in self._mydict
-
-def matchmark(colitem, matchexpr):
- return eval(matchexpr, {}, BoolDict(colitem.obj.__dict__))
-
-def pytest_configure(config):
- if config.option.strict:
- pytest.mark._config = config
-
-def skipbykeyword(colitem, keywordexpr):
- """ return True if they given keyword expression means to
- skip this collector/item.
- """
- if not keywordexpr:
- return
-
- itemkeywords = getkeywords(colitem)
- for key in filter(None, keywordexpr.split()):
- eor = key[:1] == '-'
- if eor:
- key = key[1:]
- if not (eor ^ matchonekeyword(key, itemkeywords)):
- return True
-
-def getkeywords(node):
- keywords = {}
- while node is not None:
- keywords.update(node.keywords)
- node = node.parent
- return keywords
-
-
-def matchonekeyword(key, itemkeywords):
- for elem in key.split("."):
- for kw in itemkeywords:
- if elem in kw:
- break
- else:
- return False
- return True
-
-class MarkGenerator:
- """ Factory for :class:`MarkDecorator` objects - exposed as
- a ``py.test.mark`` singleton instance. Example::
-
- import py
- @py.test.mark.slowtest
- def test_function():
- pass
-
- will set a 'slowtest' :class:`MarkInfo` object
- on the ``test_function`` object. """
-
- def __getattr__(self, name):
- if name[0] == "_":
- raise AttributeError(name)
- if hasattr(self, '_config'):
- self._check(name)
- return MarkDecorator(name)
-
- def _check(self, name):
- try:
- if name in self._markers:
- return
- except AttributeError:
- pass
- self._markers = l = set()
- for line in self._config.getini("markers"):
- beginning = line.split(":", 1)
- x = beginning[0].split("(", 1)[0]
- l.add(x)
- if name not in self._markers:
- raise AttributeError("%r not a registered marker" % (name,))
-
-class MarkDecorator:
- """ A decorator for test functions and test classes. When applied
- it will create :class:`MarkInfo` objects which may be
- :ref:`retrieved by hooks as item keywords <excontrolskip>`.
- MarkDecorator instances are often created like this::
-
- mark1 = py.test.mark.NAME # simple MarkDecorator
- mark2 = py.test.mark.NAME(name1=value) # parametrized MarkDecorator
-
- and can then be applied as decorators to test functions::
-
- @mark2
- def test_function():
- pass
- """
- def __init__(self, name, args=None, kwargs=None):
- self.markname = name
- self.args = args or ()
- self.kwargs = kwargs or {}
-
- def __repr__(self):
- d = self.__dict__.copy()
- name = d.pop('markname')
- return "<MarkDecorator %r %r>" %(name, d)
-
- def __call__(self, *args, **kwargs):
- """ if passed a single callable argument: decorate it with mark info.
- otherwise add *args/**kwargs in-place to mark information. """
- if args:
- func = args[0]
- if len(args) == 1 and hasattr(func, '__call__') or \
- hasattr(func, '__bases__'):
- if hasattr(func, '__bases__'):
- if hasattr(func, 'pytestmark'):
- l = func.pytestmark
- if not isinstance(l, list):
- func.pytestmark = [l, self]
- else:
- l.append(self)
- else:
- func.pytestmark = [self]
- else:
- holder = getattr(func, self.markname, None)
- if holder is None:
- holder = MarkInfo(self.markname, self.args, self.kwargs)
- setattr(func, self.markname, holder)
- else:
- holder.add(self.args, self.kwargs)
- return func
- kw = self.kwargs.copy()
- kw.update(kwargs)
- args = self.args + args
- return self.__class__(self.markname, args=args, kwargs=kw)
-
-class MarkInfo:
- """ Marking object created by :class:`MarkDecorator` instances. """
- def __init__(self, name, args, kwargs):
- #: name of attribute
- self.name = name
- #: positional argument list, empty if none specified
- self.args = args
- #: keyword argument dictionary, empty if nothing specified
- self.kwargs = kwargs
- self._arglist = [(args, kwargs.copy())]
-
- def __repr__(self):
- return "<MarkInfo %r args=%r kwargs=%r>" % (
- self.name, self.args, self.kwargs)
-
- def add(self, args, kwargs):
- """ add a MarkInfo with the given args and kwargs. """
- self._arglist.append((args, kwargs))
- self.args += args
- self.kwargs.update(kwargs)
-
- def __iter__(self):
- """ yield MarkInfo objects each relating to a marking-call. """
- for args, kwargs in self._arglist:
- yield MarkInfo(self.name, args, kwargs)
-
diff --git a/_pytest/monkeypatch.py b/_pytest/monkeypatch.py
deleted file mode 100644
--- a/_pytest/monkeypatch.py
+++ /dev/null
@@ -1,124 +0,0 @@
-""" monkeypatching and mocking functionality. """
-
-import os, sys
-
-def pytest_funcarg__monkeypatch(request):
- """The returned ``monkeypatch`` funcarg provides these
- helper methods to modify objects, dictionaries or os.environ::
-
- monkeypatch.setattr(obj, name, value, raising=True)
- monkeypatch.delattr(obj, name, raising=True)
- monkeypatch.setitem(mapping, name, value)
- monkeypatch.delitem(obj, name, raising=True)
- monkeypatch.setenv(name, value, prepend=False)
- monkeypatch.delenv(name, value, raising=True)
- monkeypatch.syspath_prepend(path)
- monkeypatch.chdir(path)
-
- All modifications will be undone after the requesting
- test function has finished. The ``raising``
- parameter determines if a KeyError or AttributeError
- will be raised if the set/deletion operation has no target.
- """
- mpatch = monkeypatch()
- request.addfinalizer(mpatch.undo)
- return mpatch
-
-notset = object()
-
-class monkeypatch:
- """ object keeping a record of setattr/item/env/syspath changes. """
- def __init__(self):
- self._setattr = []
- self._setitem = []
- self._cwd = None
-
- def setattr(self, obj, name, value, raising=True):
- """ set attribute ``name`` on ``obj`` to ``value``, by default
- raise AttributeEror if the attribute did not exist. """
- oldval = getattr(obj, name, notset)
- if raising and oldval is notset:
- raise AttributeError("%r has no attribute %r" %(obj, name))
- self._setattr.insert(0, (obj, name, oldval))
- setattr(obj, name, value)
-
- def delattr(self, obj, name, raising=True):
- """ delete attribute ``name`` from ``obj``, by default raise
- AttributeError it the attribute did not previously exist. """
- if not hasattr(obj, name):
- if raising:
- raise AttributeError(name)
- else:
- self._setattr.insert(0, (obj, name, getattr(obj, name, notset)))
- delattr(obj, name)
-
- def setitem(self, dic, name, value):
- """ set dictionary entry ``name`` to value. """
- self._setitem.insert(0, (dic, name, dic.get(name, notset)))
- dic[name] = value
-
- def delitem(self, dic, name, raising=True):
- """ delete ``name`` from dict, raise KeyError if it doesn't exist."""
- if name not in dic:
- if raising:
- raise KeyError(name)
- else:
- self._setitem.insert(0, (dic, name, dic.get(name, notset)))
- del dic[name]
-
- def setenv(self, name, value, prepend=None):
- """ set environment variable ``name`` to ``value``. if ``prepend``
- is a character, read the current environment variable value
- and prepend the ``value`` adjoined with the ``prepend`` character."""
- value = str(value)
- if prepend and name in os.environ:
- value = value + prepend + os.environ[name]
- self.setitem(os.environ, name, value)
-
- def delenv(self, name, raising=True):
- """ delete ``name`` from environment, raise KeyError it not exists."""
- self.delitem(os.environ, name, raising=raising)
-
- def syspath_prepend(self, path):
- """ prepend ``path`` to ``sys.path`` list of import locations. """
- if not hasattr(self, '_savesyspath'):
- self._savesyspath = sys.path[:]
- sys.path.insert(0, str(path))
-
- def chdir(self, path):
- """ change the current working directory to the specified path
- path can be a string or a py.path.local object
- """
- if self._cwd is None:
- self._cwd = os.getcwd()
- if hasattr(path, "chdir"):
- path.chdir()
- else:
- os.chdir(path)
-
- def undo(self):
- """ undo previous changes. This call consumes the
- undo stack. Calling it a second time has no effect unless
- you do more monkeypatching after the undo call."""
- for obj, name, value in self._setattr:
- if value is not notset:
- setattr(obj, name, value)
- else:
- delattr(obj, name)
- self._setattr[:] = []
- for dictionary, name, value in self._setitem:
- if value is notset:
- try:
- del dictionary[name]
- except KeyError:
- pass # was already deleted, so we have the desired state
- else:
- dictionary[name] = value
- self._setitem[:] = []
- if hasattr(self, '_savesyspath'):
- sys.path[:] = self._savesyspath
- del self._savesyspath
-
- if self._cwd is not None:
- os.chdir(self._cwd)
- self._cwd = None
diff --git a/_pytest/nose.py b/_pytest/nose.py
deleted file mode 100644
--- a/_pytest/nose.py
+++ /dev/null
@@ -1,48 +0,0 @@
-""" run test suites written for nose. """
-
-import pytest, py
-import inspect
-import sys
-
-def pytest_runtest_makereport(__multicall__, item, call):
- SkipTest = getattr(sys.modules.get('nose', None), 'SkipTest', None)
- if SkipTest:
- if call.excinfo and call.excinfo.errisinstance(SkipTest):
- # let's substitute the excinfo with a py.test.skip one
- call2 = call.__class__(lambda: py.test.skip(str(call.excinfo.value)), call.when)
- call.excinfo = call2.excinfo
-
-
- at pytest.mark.trylast
-def pytest_runtest_setup(item):
- if isinstance(item, (pytest.Function)):
- if isinstance(item.parent, pytest.Generator):
- gen = item.parent
- if not hasattr(gen, '_nosegensetup'):
- call_optional(gen.obj, 'setup')
- if isinstance(gen.parent, pytest.Instance):
- call_optional(gen.parent.obj, 'setup')
- gen._nosegensetup = True
- if not call_optional(item.obj, 'setup'):
- # call module level setup if there is no object level one
- call_optional(item.parent.obj, 'setup')
-
-def pytest_runtest_teardown(item):
- if isinstance(item, pytest.Function):
- if not call_optional(item.obj, 'teardown'):
- call_optional(item.parent.obj, 'teardown')
- #if hasattr(item.parent, '_nosegensetup'):
- # #call_optional(item._nosegensetup, 'teardown')
- # del item.parent._nosegensetup
-
-def pytest_make_collect_report(collector):
- if isinstance(collector, pytest.Generator):
- call_optional(collector.obj, 'setup')
-
-def call_optional(obj, name):
- method = getattr(obj, name, None)
- if method:
- # If there's any problems allow the exception to raise rather than
- # silently ignoring them
- method()
- return True
diff --git a/_pytest/pastebin.py b/_pytest/pastebin.py
deleted file mode 100644
--- a/_pytest/pastebin.py
+++ /dev/null
@@ -1,67 +0,0 @@
-""" submit failure or test session information to a pastebin service. """
-import py, sys
-
-class url:
- base = "http://paste.pocoo.org"
- xmlrpc = base + "/xmlrpc/"
- show = base + "/show/"
-
-def pytest_addoption(parser):
- group = parser.getgroup("terminal reporting")
- group._addoption('--pastebin', metavar="mode",
- action='store', dest="pastebin", default=None,
- type="choice", choices=['failed', 'all'],
- help="send failed|all info to Pocoo pastebin service.")
-
-def pytest_configure(__multicall__, config):
- import tempfile
- __multicall__.execute()
- if config.option.pastebin == "all":
- config._pastebinfile = tempfile.TemporaryFile('w+')
- tr = config.pluginmanager.getplugin('terminalreporter')
- oldwrite = tr._tw.write
- def tee_write(s, **kwargs):
- oldwrite(s, **kwargs)
- config._pastebinfile.write(str(s))
- tr._tw.write = tee_write
-
-def pytest_unconfigure(config):
- if hasattr(config, '_pastebinfile'):
- config._pastebinfile.seek(0)
- sessionlog = config._pastebinfile.read()
- config._pastebinfile.close()
- del config._pastebinfile
- proxyid = getproxy().newPaste("python", sessionlog)
- pastebinurl = "%s%s" % (url.show, proxyid)
- sys.stderr.write("pastebin session-log: %s\n" % pastebinurl)
- tr = config.pluginmanager.getplugin('terminalreporter')
- del tr._tw.__dict__['write']
-
-def getproxy():
- if sys.version_info < (3, 0):
- from xmlrpclib import ServerProxy
- else:
- from xmlrpc.client import ServerProxy
- return ServerProxy(url.xmlrpc).pastes
-
-def pytest_terminal_summary(terminalreporter):
- if terminalreporter.config.option.pastebin != "failed":
- return
- tr = terminalreporter
- if 'failed' in tr.stats:
- terminalreporter.write_sep("=", "Sending information to Paste Service")
- if tr.config.option.debug:
- terminalreporter.write_line("xmlrpcurl: %s" %(url.xmlrpc,))
- serverproxy = getproxy()
- for rep in terminalreporter.stats.get('failed'):
- try:
- msg = rep.longrepr.reprtraceback.reprentries[-1].reprfileloc
- except AttributeError:
- msg = tr._getfailureheadline(rep)
- tw = py.io.TerminalWriter(stringio=True)
- rep.toterminal(tw)
- s = tw.stringio.getvalue()
- assert len(s)
- proxyid = serverproxy.newPaste("python", s)
- pastebinurl = "%s%s" % (url.show, proxyid)
- tr.write_line("%s --> %s" %(msg, pastebinurl))
diff --git a/_pytest/pdb.py b/_pytest/pdb.py
deleted file mode 100644
--- a/_pytest/pdb.py
+++ /dev/null
@@ -1,95 +0,0 @@
-""" interactive debugging with PDB, the Python Debugger. """
-
-import pytest, py
-import sys
-
-def pytest_addoption(parser):
- group = parser.getgroup("general")
- group._addoption('--pdb',
- action="store_true", dest="usepdb", default=False,
- help="start the interactive Python debugger on errors.")
-
-def pytest_namespace():
- return {'set_trace': pytestPDB().set_trace}
-
-def pytest_configure(config):
- if config.getvalue("usepdb"):
- config.pluginmanager.register(PdbInvoke(), 'pdbinvoke')
-
-class pytestPDB:
- """ Pseudo PDB that defers to the real pdb. """
- item = None
- collector = None
-
- def set_trace(self):
- """ invoke PDB set_trace debugging, dropping any IO capturing. """
- frame = sys._getframe().f_back
- item = self.item or self.collector
-
- if item is not None:
- capman = item.config.pluginmanager.getplugin("capturemanager")
- out, err = capman.suspendcapture()
- if hasattr(item, 'outerr'):
- item.outerr = (item.outerr[0] + out, item.outerr[1] + err)
- tw = py.io.TerminalWriter()
- tw.line()
- tw.sep(">", "PDB set_trace (IO-capturing turned off)")
- py.std.pdb.Pdb().set_trace(frame)
-
-def pdbitem(item):
- pytestPDB.item = item
-pytest_runtest_setup = pytest_runtest_call = pytest_runtest_teardown = pdbitem
-
- at pytest.mark.tryfirst
-def pytest_make_collect_report(__multicall__, collector):
- try:
- pytestPDB.collector = collector
- return __multicall__.execute()
- finally:
- pytestPDB.collector = None
-
-def pytest_runtest_makereport():
- pytestPDB.item = None
-
-class PdbInvoke:
- @pytest.mark.tryfirst
- def pytest_runtest_makereport(self, item, call, __multicall__):
- rep = __multicall__.execute()
- if not call.excinfo or \
- call.excinfo.errisinstance(pytest.skip.Exception) or \
- call.excinfo.errisinstance(py.std.bdb.BdbQuit):
- return rep
- if "xfail" in rep.keywords:
- return rep
- # we assume that the above execute() suspended capturing
- # XXX we re-use the TerminalReporter's terminalwriter
- # because this seems to avoid some encoding related troubles
- # for not completely clear reasons.
- tw = item.config.pluginmanager.getplugin("terminalreporter")._tw
- tw.line()
- tw.sep(">", "traceback")
- rep.toterminal(tw)
- tw.sep(">", "entering PDB")
- # A doctest.UnexpectedException is not useful for post_mortem.
- # Use the underlying exception instead:
- if isinstance(call.excinfo.value, py.std.doctest.UnexpectedException):
- tb = call.excinfo.value.exc_info[2]
- else:
- tb = call.excinfo._excinfo[2]
- post_mortem(tb)
- rep._pdbshown = True
- return rep
-
-def post_mortem(t):
- pdb = py.std.pdb
- class Pdb(pdb.Pdb):
- def get_stack(self, f, t):
- stack, i = pdb.Pdb.get_stack(self, f, t)
- if f is None:
- i = max(0, len(stack) - 1)
- while i and stack[i][0].f_locals.get("__tracebackhide__", False):
- i-=1
- return stack, i
- p = Pdb()
- p.reset()
- p.interaction(None, t)
diff --git a/_pytest/pytester.py b/_pytest/pytester.py
deleted file mode 100644
--- a/_pytest/pytester.py
+++ /dev/null
@@ -1,680 +0,0 @@
-""" (disabled by default) support for testing py.test and py.test plugins. """
-
-import py, pytest
-import sys, os
-import re
-import inspect
-import time
-from fnmatch import fnmatch
-from _pytest.main import Session, EXIT_OK
-from py.builtin import print_
-from _pytest.core import HookRelay
-
-def pytest_addoption(parser):
- group = parser.getgroup("pylib")
- group.addoption('--no-tools-on-path',
- action="store_true", dest="notoolsonpath", default=False,
- help=("discover tools on PATH instead of going through py.cmdline.")
- )
-
-def pytest_configure(config):
- # This might be called multiple times. Only take the first.
- global _pytest_fullpath
- import pytest
- try:
- _pytest_fullpath
- except NameError:
- _pytest_fullpath = os.path.abspath(pytest.__file__.rstrip("oc"))
- _pytest_fullpath = _pytest_fullpath.replace("$py.class", ".py")
-
-def pytest_funcarg___pytest(request):
- return PytestArg(request)
-
-class PytestArg:
- def __init__(self, request):
- self.request = request
-
- def gethookrecorder(self, hook):
- hookrecorder = HookRecorder(hook._pm)
- hookrecorder.start_recording(hook._hookspecs)
- self.request.addfinalizer(hookrecorder.finish_recording)
- return hookrecorder
-
-class ParsedCall:
- def __init__(self, name, locals):
- assert '_name' not in locals
- self.__dict__.update(locals)
- self.__dict__.pop('self')
- self._name = name
-
- def __repr__(self):
- d = self.__dict__.copy()
- del d['_name']
- return "<ParsedCall %r(**%r)>" %(self._name, d)
-
-class HookRecorder:
- def __init__(self, pluginmanager):
- self._pluginmanager = pluginmanager
- self.calls = []
- self._recorders = {}
-
- def start_recording(self, hookspecs):
- if not isinstance(hookspecs, (list, tuple)):
- hookspecs = [hookspecs]
- for hookspec in hookspecs:
- assert hookspec not in self._recorders
- class RecordCalls:
- _recorder = self
- for name, method in vars(hookspec).items():
- if name[0] != "_":
- setattr(RecordCalls, name, self._makecallparser(method))
- recorder = RecordCalls()
- self._recorders[hookspec] = recorder
- self._pluginmanager.register(recorder)
- self.hook = HookRelay(hookspecs, pm=self._pluginmanager,
- prefix="pytest_")
-
- def finish_recording(self):
- for recorder in self._recorders.values():
- self._pluginmanager.unregister(recorder)
- self._recorders.clear()
-
- def _makecallparser(self, method):
- name = method.__name__
- args, varargs, varkw, default = py.std.inspect.getargspec(method)
- if not args or args[0] != "self":
- args.insert(0, 'self')
- fspec = py.std.inspect.formatargspec(args, varargs, varkw, default)
- # we use exec because we want to have early type
- # errors on wrong input arguments, using
- # *args/**kwargs delays this and gives errors
- # elsewhere
- exec (py.code.compile("""
- def %(name)s%(fspec)s:
- self._recorder.calls.append(
- ParsedCall(%(name)r, locals()))
- """ % locals()))
- return locals()[name]
-
- def getcalls(self, names):
- if isinstance(names, str):
- names = names.split()
- for name in names:
- for cls in self._recorders:
- if name in vars(cls):
- break
- else:
- raise ValueError("callname %r not found in %r" %(
- name, self._recorders.keys()))
- l = []
- for call in self.calls:
- if call._name in names:
- l.append(call)
- return l
-
- def contains(self, entries):
- __tracebackhide__ = True
- from py.builtin import print_
- i = 0
- entries = list(entries)
- backlocals = py.std.sys._getframe(1).f_locals
- while entries:
- name, check = entries.pop(0)
- for ind, call in enumerate(self.calls[i:]):
- if call._name == name:
- print_("NAMEMATCH", name, call)
- if eval(check, backlocals, call.__dict__):
- print_("CHECKERMATCH", repr(check), "->", call)
- else:
- print_("NOCHECKERMATCH", repr(check), "-", call)
- continue
- i += ind + 1
- break
- print_("NONAMEMATCH", name, "with", call)
- else:
- py.test.fail("could not find %r check %r" % (name, check))
-
- def popcall(self, name):
- __tracebackhide__ = True
- for i, call in enumerate(self.calls):
- if call._name == name:
- del self.calls[i]
- return call
- lines = ["could not find call %r, in:" % (name,)]
- lines.extend([" %s" % str(x) for x in self.calls])
- py.test.fail("\n".join(lines))
-
- def getcall(self, name):
- l = self.getcalls(name)
- assert len(l) == 1, (name, l)
- return l[0]
-
-
-def pytest_funcarg__linecomp(request):
- return LineComp()
-
-def pytest_funcarg__LineMatcher(request):
- return LineMatcher
-
-def pytest_funcarg__testdir(request):
- tmptestdir = TmpTestdir(request)
- return tmptestdir
-
-rex_outcome = re.compile("(\d+) (\w+)")
-class RunResult:
- def __init__(self, ret, outlines, errlines, duration):
- self.ret = ret
- self.outlines = outlines
- self.errlines = errlines
- self.stdout = LineMatcher(outlines)
- self.stderr = LineMatcher(errlines)
- self.duration = duration
-
- def parseoutcomes(self):
- for line in reversed(self.outlines):
- if 'seconds' in line:
- outcomes = rex_outcome.findall(line)
- if outcomes:
- d = {}
- for num, cat in outcomes:
- d[cat] = int(num)
- return d
-
-class TmpTestdir:
- def __init__(self, request):
- self.request = request
- self.Config = request.config.__class__
- self._pytest = request.getfuncargvalue("_pytest")
- # XXX remove duplication with tmpdir plugin
- basetmp = request.config._tmpdirhandler.ensuretemp("testdir")
- name = request.function.__name__
- for i in range(100):
- try:
- tmpdir = basetmp.mkdir(name + str(i))
- except py.error.EEXIST:
- continue
- break
- # we need to create another subdir
- # because Directory.collect() currently loads
- # conftest.py from sibling directories
- self.tmpdir = tmpdir.mkdir(name)
- self.plugins = []
- self._syspathremove = []
- self.chdir() # always chdir
- self.request.addfinalizer(self.finalize)
-
- def __repr__(self):
- return "<TmpTestdir %r>" % (self.tmpdir,)
-
- def finalize(self):
- for p in self._syspathremove:
- py.std.sys.path.remove(p)
- if hasattr(self, '_olddir'):
- self._olddir.chdir()
- # delete modules that have been loaded from tmpdir
- for name, mod in list(sys.modules.items()):
- if mod:
- fn = getattr(mod, '__file__', None)
- if fn and fn.startswith(str(self.tmpdir)):
- del sys.modules[name]
-
- def getreportrecorder(self, obj):
- if hasattr(obj, 'config'):
- obj = obj.config
- if hasattr(obj, 'hook'):
- obj = obj.hook
- assert hasattr(obj, '_hookspecs'), obj
- reprec = ReportRecorder(obj)
- reprec.hookrecorder = self._pytest.gethookrecorder(obj)
- reprec.hook = reprec.hookrecorder.hook
- return reprec
-
- def chdir(self):
- old = self.tmpdir.chdir()
- if not hasattr(self, '_olddir'):
- self._olddir = old
-
- def _makefile(self, ext, args, kwargs):
- items = list(kwargs.items())
- if args:
- source = py.builtin._totext("\n").join(
- map(py.builtin._totext, args)) + py.builtin._totext("\n")
- basename = self.request.function.__name__
- items.insert(0, (basename, source))
- ret = None
- for name, value in items:
- p = self.tmpdir.join(name).new(ext=ext)
- source = py.builtin._totext(py.code.Source(value)).lstrip()
- p.write(source.encode("utf-8"), "wb")
- if ret is None:
- ret = p
- return ret
-
-
- def makefile(self, ext, *args, **kwargs):
- return self._makefile(ext, args, kwargs)
-
- def makeini(self, source):
- return self.makefile('cfg', setup=source)
-
- def makeconftest(self, source):
- return self.makepyfile(conftest=source)
-
- def makeini(self, source):
- return self.makefile('.ini', tox=source)
-
- def getinicfg(self, source):
- p = self.makeini(source)
- return py.iniconfig.IniConfig(p)['pytest']
-
- def makepyfile(self, *args, **kwargs):
- return self._makefile('.py', args, kwargs)
-
- def maketxtfile(self, *args, **kwargs):
- return self._makefile('.txt', args, kwargs)
-
- def syspathinsert(self, path=None):
- if path is None:
- path = self.tmpdir
- py.std.sys.path.insert(0, str(path))
- self._syspathremove.append(str(path))
-
- def mkdir(self, name):
- return self.tmpdir.mkdir(name)
-
- def mkpydir(self, name):
- p = self.mkdir(name)
- p.ensure("__init__.py")
- return p
-
- Session = Session
- def getnode(self, config, arg):
- session = Session(config)
- assert '::' not in str(arg)
- p = py.path.local(arg)
- x = session.fspath.bestrelpath(p)
- config.hook.pytest_sessionstart(session=session)
- res = session.perform_collect([x], genitems=False)[0]
- config.hook.pytest_sessionfinish(session=session, exitstatus=EXIT_OK)
- return res
-
- def getpathnode(self, path):
- config = self.parseconfigure(path)
- session = Session(config)
- x = session.fspath.bestrelpath(path)
- config.hook.pytest_sessionstart(session=session)
- res = session.perform_collect([x], genitems=False)[0]
- config.hook.pytest_sessionfinish(session=session, exitstatus=EXIT_OK)
- return res
-
- def genitems(self, colitems):
- session = colitems[0].session
- result = []
- for colitem in colitems:
- result.extend(session.genitems(colitem))
- return result
-
- def runitem(self, source):
- # used from runner functional tests
- item = self.getitem(source)
- # the test class where we are called from wants to provide the runner
- testclassinstance = py.builtin._getimself(self.request.function)
- runner = testclassinstance.getrunner()
- return runner(item)
-
- def inline_runsource(self, source, *cmdlineargs):
- p = self.makepyfile(source)
- l = list(cmdlineargs) + [p]
- return self.inline_run(*l)
-
- def inline_runsource1(self, *args):
- args = list(args)
- source = args.pop()
- p = self.makepyfile(source)
- l = list(args) + [p]
- reprec = self.inline_run(*l)
- reports = reprec.getreports("pytest_runtest_logreport")
- assert len(reports) == 3, reports # setup/call/teardown
- return reports[1]
-
- def inline_genitems(self, *args):
- return self.inprocess_run(list(args) + ['--collectonly'])
-
- def inline_run(self, *args):
- items, rec = self.inprocess_run(args)
- return rec
-
- def inprocess_run(self, args, plugins=None):
- rec = []
- items = []
- class Collect:
- def pytest_configure(x, config):
- rec.append(self.getreportrecorder(config))
- def pytest_itemcollected(self, item):
- items.append(item)
- if not plugins:
- plugins = []
- plugins.append(Collect())
- ret = self.pytestmain(list(args), plugins=[Collect()])
- reprec = rec[0]
- reprec.ret = ret
- assert len(rec) == 1
- return items, reprec
-
- def parseconfig(self, *args):
- args = [str(x) for x in args]
- for x in args:
- if str(x).startswith('--basetemp'):
- break
- else:
- args.append("--basetemp=%s" % self.tmpdir.dirpath('basetemp'))
- import _pytest.core
- config = _pytest.core._prepareconfig(args, self.plugins)
- # the in-process pytest invocation needs to avoid leaking FDs
- # so we register a "reset_capturings" callmon the capturing manager
- # and make sure it gets called
- config._cleanup.append(
- config.pluginmanager.getplugin("capturemanager").reset_capturings)
- import _pytest.config
- self.request.addfinalizer(
- lambda: _pytest.config.pytest_unconfigure(config))
- return config
-
- def parseconfigure(self, *args):
- config = self.parseconfig(*args)
- config.pluginmanager.do_configure(config)
- self.request.addfinalizer(lambda:
- config.pluginmanager.do_unconfigure(config))
- return config
-
- def getitem(self, source, funcname="test_func"):
- for item in self.getitems(source):
- if item.name == funcname:
- return item
- assert 0, "%r item not found in module:\n%s" %(funcname, source)
-
- def getitems(self, source):
- modcol = self.getmodulecol(source)
- return self.genitems([modcol])
-
- def getmodulecol(self, source, configargs=(), withinit=False):
- kw = {self.request.function.__name__: py.code.Source(source).strip()}
- path = self.makepyfile(**kw)
- if withinit:
- self.makepyfile(__init__ = "#")
- self.config = config = self.parseconfigure(path, *configargs)
- node = self.getnode(config, path)
- return node
-
- def collect_by_name(self, modcol, name):
- for colitem in modcol._memocollect():
- if colitem.name == name:
- return colitem
-
- def popen(self, cmdargs, stdout, stderr, **kw):
- env = os.environ.copy()
- env['PYTHONPATH'] = os.pathsep.join(filter(None, [
- str(os.getcwd()), env.get('PYTHONPATH', '')]))
- kw['env'] = env
- #print "env", env
- return py.std.subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw)
-
- def pytestmain(self, *args, **kwargs):
- class ResetCapturing:
- @pytest.mark.trylast
- def pytest_unconfigure(self, config):
- capman = config.pluginmanager.getplugin("capturemanager")
- capman.reset_capturings()
- plugins = kwargs.setdefault("plugins", [])
- rc = ResetCapturing()
- plugins.append(rc)
- return pytest.main(*args, **kwargs)
-
- def run(self, *cmdargs):
- return self._run(*cmdargs)
-
- def _run(self, *cmdargs):
- cmdargs = [str(x) for x in cmdargs]
- p1 = self.tmpdir.join("stdout")
- p2 = self.tmpdir.join("stderr")
- print_("running", cmdargs, "curdir=", py.path.local())
- f1 = p1.open("wb")
- f2 = p2.open("wb")
- now = time.time()
- popen = self.popen(cmdargs, stdout=f1, stderr=f2,
- close_fds=(sys.platform != "win32"))
- ret = popen.wait()
- f1.close()
- f2.close()
- out = p1.read("rb")
- out = getdecoded(out).splitlines()
- err = p2.read("rb")
- err = getdecoded(err).splitlines()
- def dump_lines(lines, fp):
- try:
- for line in lines:
- py.builtin.print_(line, file=fp)
- except UnicodeEncodeError:
- print("couldn't print to %s because of encoding" % (fp,))
- dump_lines(out, sys.stdout)
- dump_lines(err, sys.stderr)
- return RunResult(ret, out, err, time.time()-now)
-
- def runpybin(self, scriptname, *args):
- fullargs = self._getpybinargs(scriptname) + args
- return self.run(*fullargs)
-
- def _getpybinargs(self, scriptname):
- if not self.request.config.getvalue("notoolsonpath"):
- # XXX we rely on script refering to the correct environment
- # we cannot use "(py.std.sys.executable,script)"
- # becaue on windows the script is e.g. a py.test.exe
- return (py.std.sys.executable, _pytest_fullpath,)
- else:
- py.test.skip("cannot run %r with --no-tools-on-path" % scriptname)
-
- def runpython(self, script, prepend=True):
- if prepend:
- s = self._getsysprepend()
- if s:
- script.write(s + "\n" + script.read())
- return self.run(sys.executable, script)
-
- def _getsysprepend(self):
- if self.request.config.getvalue("notoolsonpath"):
- s = "import sys;sys.path.insert(0,%r);" % str(py._pydir.dirpath())
- else:
- s = ""
- return s
-
- def runpython_c(self, command):
- command = self._getsysprepend() + command
- return self.run(py.std.sys.executable, "-c", command)
-
- def runpytest(self, *args):
- p = py.path.local.make_numbered_dir(prefix="runpytest-",
- keep=None, rootdir=self.tmpdir)
- args = ('--basetemp=%s' % p, ) + args
- #for x in args:
- # if '--confcutdir' in str(x):
- # break
- #else:
- # pass
- # args = ('--confcutdir=.',) + args
- plugins = [x for x in self.plugins if isinstance(x, str)]
- if plugins:
- args = ('-p', plugins[0]) + args
- return self.runpybin("py.test", *args)
-
- def spawn_pytest(self, string, expect_timeout=10.0):
- if self.request.config.getvalue("notoolsonpath"):
- py.test.skip("--no-tools-on-path prevents running pexpect-spawn tests")
- basetemp = self.tmpdir.mkdir("pexpect")
- invoke = " ".join(map(str, self._getpybinargs("py.test")))
- cmd = "%s --basetemp=%s %s" % (invoke, basetemp, string)
- return self.spawn(cmd, expect_timeout=expect_timeout)
-
- def spawn(self, cmd, expect_timeout=10.0):
- pexpect = py.test.importorskip("pexpect", "2.4")
- if hasattr(sys, 'pypy_version_info') and '64' in py.std.platform.machine():
- pytest.skip("pypy-64 bit not supported")
- if sys.platform == "darwin":
- pytest.xfail("pexpect does not work reliably on darwin?!")
- logfile = self.tmpdir.join("spawn.out")
- child = pexpect.spawn(cmd, logfile=logfile.open("w"))
- child.timeout = expect_timeout
- return child
-
-def getdecoded(out):
- try:
- return out.decode("utf-8")
- except UnicodeDecodeError:
- return "INTERNAL not-utf8-decodeable, truncated string:\n%s" % (
- py.io.saferepr(out),)
-
-class ReportRecorder(object):
- def __init__(self, hook):
- self.hook = hook
- self.pluginmanager = hook._pm
- self.pluginmanager.register(self)
-
- def getcall(self, name):
- return self.hookrecorder.getcall(name)
-
- def popcall(self, name):
- return self.hookrecorder.popcall(name)
-
- def getcalls(self, names):
- """ return list of ParsedCall instances matching the given eventname. """
- return self.hookrecorder.getcalls(names)
-
- # functionality for test reports
-
- def getreports(self, names="pytest_runtest_logreport pytest_collectreport"):
- return [x.report for x in self.getcalls(names)]
-
- def matchreport(self, inamepart="",
- names="pytest_runtest_logreport pytest_collectreport", when=None):
- """ return a testreport whose dotted import path matches """
- l = []
- for rep in self.getreports(names=names):
- try:
- if not when and rep.when != "call" and rep.passed:
- # setup/teardown passing reports - let's ignore those
- continue
- except AttributeError:
- pass
- if when and getattr(rep, 'when', None) != when:
- continue
- if not inamepart or inamepart in rep.nodeid.split("::"):
- l.append(rep)
- if not l:
- raise ValueError("could not find test report matching %r: no test reports at all!" %
- (inamepart,))
- if len(l) > 1:
- raise ValueError("found more than one testreport matching %r: %s" %(
- inamepart, l))
- return l[0]
-
- def getfailures(self, names='pytest_runtest_logreport pytest_collectreport'):
- return [rep for rep in self.getreports(names) if rep.failed]
-
- def getfailedcollections(self):
- return self.getfailures('pytest_collectreport')
-
- def listoutcomes(self):
- passed = []
- skipped = []
- failed = []
- for rep in self.getreports("pytest_runtest_logreport"):
- if rep.passed:
- if rep.when == "call":
- passed.append(rep)
- elif rep.skipped:
- skipped.append(rep)
- elif rep.failed:
- failed.append(rep)
- return passed, skipped, failed
-
- def countoutcomes(self):
- return [len(x) for x in self.listoutcomes()]
-
- def assertoutcome(self, passed=0, skipped=0, failed=0):
- realpassed, realskipped, realfailed = self.listoutcomes()
- assert passed == len(realpassed)
- assert skipped == len(realskipped)
- assert failed == len(realfailed)
-
- def clear(self):
- self.hookrecorder.calls[:] = []
-
- def unregister(self):
- self.pluginmanager.unregister(self)
- self.hookrecorder.finish_recording()
-
-class LineComp:
- def __init__(self):
- self.stringio = py.io.TextIO()
-
- def assert_contains_lines(self, lines2):
- """ assert that lines2 are contained (linearly) in lines1.
- return a list of extralines found.
- """
- __tracebackhide__ = True
- val = self.stringio.getvalue()
- self.stringio.truncate(0)
- self.stringio.seek(0)
- lines1 = val.split("\n")
- return LineMatcher(lines1).fnmatch_lines(lines2)
-
-class LineMatcher:
- def __init__(self, lines):
- self.lines = lines
-
- def str(self):
- return "\n".join(self.lines)
-
- def _getlines(self, lines2):
- if isinstance(lines2, str):
- lines2 = py.code.Source(lines2)
- if isinstance(lines2, py.code.Source):
- lines2 = lines2.strip().lines
- return lines2
-
- def fnmatch_lines_random(self, lines2):
- lines2 = self._getlines(lines2)
- for line in lines2:
- for x in self.lines:
- if line == x or fnmatch(x, line):
- print_("matched: ", repr(line))
- break
- else:
- raise ValueError("line %r not found in output" % line)
-
- def fnmatch_lines(self, lines2):
- def show(arg1, arg2):
- py.builtin.print_(arg1, arg2, file=py.std.sys.stderr)
- lines2 = self._getlines(lines2)
- lines1 = self.lines[:]
- nextline = None
- extralines = []
- __tracebackhide__ = True
- for line in lines2:
- nomatchprinted = False
- while lines1:
- nextline = lines1.pop(0)
- if line == nextline:
- show("exact match:", repr(line))
- break
- elif fnmatch(nextline, line):
- show("fnmatch:", repr(line))
- show(" with:", repr(nextline))
- break
- else:
- if not nomatchprinted:
- show("nomatch:", repr(line))
- nomatchprinted = True
- show(" and:", repr(nextline))
- extralines.append(nextline)
- else:
- py.test.fail("remains unmatched: %r, see stderr" % (line,))
diff --git a/_pytest/python.py b/_pytest/python.py
deleted file mode 100644
--- a/_pytest/python.py
+++ /dev/null
@@ -1,1009 +0,0 @@
-""" Python test discovery, setup and run of test functions. """
-import py
-import inspect
-import sys
-import pytest
-from py._code.code import TerminalRepr
-from _pytest.monkeypatch import monkeypatch
-
-import _pytest
-cutdir = py.path.local(_pytest.__file__).dirpath()
-
-def pytest_addoption(parser):
- group = parser.getgroup("general")
- group.addoption('--funcargs',
- action="store_true", dest="showfuncargs", default=False,
- help="show available function arguments, sorted by plugin")
- parser.addini("python_files", type="args",
- default=('test_*.py', '*_test.py'),
- help="glob-style file patterns for Python test module discovery")
- parser.addini("python_classes", type="args", default=("Test",),
- help="prefixes for Python test class discovery")
- parser.addini("python_functions", type="args", default=("test",),
- help="prefixes for Python test function and method discovery")
-
-def pytest_cmdline_main(config):
- if config.option.showfuncargs:
- showfuncargs(config)
- return 0
-
-
-def pytest_generate_tests(metafunc):
- try:
- param = metafunc.function.parametrize
- except AttributeError:
- return
- for p in param:
- metafunc.parametrize(*p.args, **p.kwargs)
-
-def pytest_configure(config):
- config.addinivalue_line("markers",
- "parametrize(argnames, argvalues): call a test function multiple "
- "times passing in multiple different argument value sets. Example: "
- "@parametrize('arg1', [1,2]) would lead to two calls of the decorated "
- "test function, one with arg1=1 and another with arg1=2."
- )
-
-
- at pytest.mark.trylast
-def pytest_namespace():
- raises.Exception = pytest.fail.Exception
- return {
- 'raises' : raises,
- 'collect': {
- 'Module': Module, 'Class': Class, 'Instance': Instance,
- 'Function': Function, 'Generator': Generator,
- '_fillfuncargs': fillfuncargs}
- }
-
-def pytest_funcarg__pytestconfig(request):
- """ the pytest config object with access to command line opts."""
- return request.config
-
-def pytest_pyfunc_call(__multicall__, pyfuncitem):
- if not __multicall__.execute():
- testfunction = pyfuncitem.obj
- if pyfuncitem._isyieldedfunction():
- testfunction(*pyfuncitem._args)
- else:
- funcargs = pyfuncitem.funcargs
- testfunction(**funcargs)
-
-def pytest_collect_file(path, parent):
- ext = path.ext
- pb = path.purebasename
- if ext == ".py":
- if not parent.session.isinitpath(path):
- for pat in parent.config.getini('python_files'):
- if path.fnmatch(pat):
- break
- else:
- return
- return parent.ihook.pytest_pycollect_makemodule(
- path=path, parent=parent)
-
-def pytest_pycollect_makemodule(path, parent):
- return Module(path, parent)
-
-def pytest_pycollect_makeitem(__multicall__, collector, name, obj):
- res = __multicall__.execute()
- if res is not None:
- return res
- if inspect.isclass(obj):
- #if hasattr(collector.obj, 'unittest'):
- # return # we assume it's a mixin class for a TestCase derived one
- if collector.classnamefilter(name):
- if not hasinit(obj):
- Class = collector._getcustomclass("Class")
- return Class(name, parent=collector)
- elif collector.funcnamefilter(name) and hasattr(obj, '__call__'):
- if is_generator(obj):
- return Generator(name, parent=collector)
- else:
- return collector._genfunctions(name, obj)
-
-def is_generator(func):
- try:
- return py.code.getrawcode(func).co_flags & 32 # generator function
- except AttributeError: # builtin functions have no bytecode
- # assume them to not be generators
- return False
-
-class PyobjMixin(object):
- def obj():
- def fget(self):
- try:
- return self._obj
- except AttributeError:
- self._obj = obj = self._getobj()
- return obj
- def fset(self, value):
- self._obj = value
- return property(fget, fset, None, "underlying python object")
- obj = obj()
-
- def _getobj(self):
- return getattr(self.parent.obj, self.name)
-
- def getmodpath(self, stopatmodule=True, includemodule=False):
- """ return python path relative to the containing module. """
- chain = self.listchain()
- chain.reverse()
- parts = []
- for node in chain:
- if isinstance(node, Instance):
- continue
- name = node.name
- if isinstance(node, Module):
- assert name.endswith(".py")
- name = name[:-3]
- if stopatmodule:
- if includemodule:
- parts.append(name)
- break
- parts.append(name)
- parts.reverse()
- s = ".".join(parts)
- return s.replace(".[", "[")
-
- def _getfslineno(self):
- try:
- return self._fslineno
- except AttributeError:
- pass
- obj = self.obj
- # xxx let decorators etc specify a sane ordering
- if hasattr(obj, 'place_as'):
- obj = obj.place_as
-
- self._fslineno = py.code.getfslineno(obj)
- assert isinstance(self._fslineno[1], int), obj
- return self._fslineno
-
- def reportinfo(self):
- # XXX caching?
- obj = self.obj
- if hasattr(obj, 'compat_co_firstlineno'):
- # nose compatibility
- fspath = sys.modules[obj.__module__].__file__
- if fspath.endswith(".pyc"):
- fspath = fspath[:-1]
- #assert 0
- #fn = inspect.getsourcefile(obj) or inspect.getfile(obj)
- lineno = obj.compat_co_firstlineno
- modpath = obj.__module__
- else:
- fspath, lineno = self._getfslineno()
- modpath = self.getmodpath()
- assert isinstance(lineno, int)
- return fspath, lineno, modpath
-
-class PyCollectorMixin(PyobjMixin, pytest.Collector):
-
- def funcnamefilter(self, name):
- for prefix in self.config.getini("python_functions"):
- if name.startswith(prefix):
- return True
-
- def classnamefilter(self, name):
- for prefix in self.config.getini("python_classes"):
- if name.startswith(prefix):
- return True
-
- def collect(self):
- # NB. we avoid random getattrs and peek in the __dict__ instead
- # (XXX originally introduced from a PyPy need, still true?)
- dicts = [getattr(self.obj, '__dict__', {})]
- for basecls in inspect.getmro(self.obj.__class__):
- dicts.append(basecls.__dict__)
- seen = {}
- l = []
- for dic in dicts:
- for name, obj in dic.items():
- if name in seen:
- continue
- seen[name] = True
- if name[0] != "_":
- res = self.makeitem(name, obj)
- if res is None:
- continue
- if not isinstance(res, list):
- res = [res]
- l.extend(res)
- l.sort(key=lambda item: item.reportinfo()[:2])
- return l
-
- def makeitem(self, name, obj):
- return self.ihook.pytest_pycollect_makeitem(
- collector=self, name=name, obj=obj)
-
- def _genfunctions(self, name, funcobj):
- module = self.getparent(Module).obj
- clscol = self.getparent(Class)
- cls = clscol and clscol.obj or None
- transfer_markers(funcobj, cls, module)
- metafunc = Metafunc(funcobj, config=self.config,
- cls=cls, module=module)
- gentesthook = self.config.hook.pytest_generate_tests
- extra = [module]
- if cls is not None:
- extra.append(cls())
- plugins = self.getplugins() + extra
- gentesthook.pcall(plugins, metafunc=metafunc)
- Function = self._getcustomclass("Function")
- if not metafunc._calls:
- return Function(name, parent=self)
- l = []
- for callspec in metafunc._calls:
- subname = "%s[%s]" %(name, callspec.id)
- function = Function(name=subname, parent=self,
- callspec=callspec, callobj=funcobj, keywords={callspec.id:True})
- l.append(function)
- return l
-
-def transfer_markers(funcobj, cls, mod):
- # XXX this should rather be code in the mark plugin or the mark
- # plugin should merge with the python plugin.
- for holder in (cls, mod):
- try:
- pytestmark = holder.pytestmark
- except AttributeError:
- continue
- if isinstance(pytestmark, list):
- for mark in pytestmark:
- mark(funcobj)
- else:
- pytestmark(funcobj)
-
-class Module(pytest.File, PyCollectorMixin):
- def _getobj(self):
- return self._memoizedcall('_obj', self._importtestmodule)
-
- def _importtestmodule(self):
- # we assume we are only called once per module
- try:
- mod = self.fspath.pyimport(ensuresyspath=True)
- except SyntaxError:
- excinfo = py.code.ExceptionInfo()
- raise self.CollectError(excinfo.getrepr(style="short"))
- except self.fspath.ImportMismatchError:
- e = sys.exc_info()[1]
- raise self.CollectError(
- "import file mismatch:\n"
- "imported module %r has this __file__ attribute:\n"
- " %s\n"
- "which is not the same as the test file we want to collect:\n"
- " %s\n"
- "HINT: remove __pycache__ / .pyc files and/or use a "
- "unique basename for your test file modules"
- % e.args
- )
- #print "imported test module", mod
- self.config.pluginmanager.consider_module(mod)
- return mod
-
- def setup(self):
- if hasattr(self.obj, 'setup_module'):
- #XXX: nose compat hack, move to nose plugin
- # if it takes a positional arg, its probably a pytest style one
- # so we pass the current module object
- if inspect.getargspec(self.obj.setup_module)[0]:
- self.obj.setup_module(self.obj)
- else:
- self.obj.setup_module()
-
- def teardown(self):
- if hasattr(self.obj, 'teardown_module'):
- #XXX: nose compat hack, move to nose plugin
- # if it takes a positional arg, its probably a py.test style one
- # so we pass the current module object
- if inspect.getargspec(self.obj.teardown_module)[0]:
- self.obj.teardown_module(self.obj)
- else:
- self.obj.teardown_module()
-
-class Class(PyCollectorMixin, pytest.Collector):
-
- def collect(self):
- return [self._getcustomclass("Instance")(name="()", parent=self)]
-
- def setup(self):
- setup_class = getattr(self.obj, 'setup_class', None)
- if setup_class is not None:
- setup_class = getattr(setup_class, 'im_func', setup_class)
- setup_class(self.obj)
-
- def teardown(self):
- teardown_class = getattr(self.obj, 'teardown_class', None)
- if teardown_class is not None:
- teardown_class = getattr(teardown_class, 'im_func', teardown_class)
- teardown_class(self.obj)
-
-class Instance(PyCollectorMixin, pytest.Collector):
- def _getobj(self):
- return self.parent.obj()
-
- def newinstance(self):
- self.obj = self._getobj()
- return self.obj
-
-class FunctionMixin(PyobjMixin):
- """ mixin for the code common to Function and Generator.
- """
- def setup(self):
- """ perform setup for this test function. """
- if hasattr(self, '_preservedparent'):
- obj = self._preservedparent
- elif isinstance(self.parent, Instance):
- obj = self.parent.newinstance()
- self.obj = self._getobj()
- else:
- obj = self.parent.obj
- if inspect.ismethod(self.obj):
- name = 'setup_method'
- else:
- name = 'setup_function'
- setup_func_or_method = getattr(obj, name, None)
- if setup_func_or_method is not None:
- setup_func_or_method(self.obj)
-
- def teardown(self):
- """ perform teardown for this test function. """
- if inspect.ismethod(self.obj):
- name = 'teardown_method'
- else:
- name = 'teardown_function'
- obj = self.parent.obj
- teardown_func_or_meth = getattr(obj, name, None)
- if teardown_func_or_meth is not None:
- teardown_func_or_meth(self.obj)
-
- def _prunetraceback(self, excinfo):
- if hasattr(self, '_obj') and not self.config.option.fulltrace:
- code = py.code.Code(self.obj)
- path, firstlineno = code.path, code.firstlineno
- traceback = excinfo.traceback
- ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
- if ntraceback == traceback:
- ntraceback = ntraceback.cut(path=path)
- if ntraceback == traceback:
- ntraceback = ntraceback.cut(excludepath=cutdir)
- excinfo.traceback = ntraceback.filter()
-
- def _repr_failure_py(self, excinfo, style="long"):
- if excinfo.errisinstance(FuncargRequest.LookupError):
- fspath, lineno, msg = self.reportinfo()
- lines, _ = inspect.getsourcelines(self.obj)
- for i, line in enumerate(lines):
- if line.strip().startswith('def'):
- return FuncargLookupErrorRepr(fspath, lineno,
- lines[:i+1], str(excinfo.value))
- if excinfo.errisinstance(pytest.fail.Exception):
- if not excinfo.value.pytrace:
- return str(excinfo.value)
- return super(FunctionMixin, self)._repr_failure_py(excinfo,
- style=style)
-
- def repr_failure(self, excinfo, outerr=None):
- assert outerr is None, "XXX outerr usage is deprecated"
- return self._repr_failure_py(excinfo,
- style=self.config.option.tbstyle)
-
-class FuncargLookupErrorRepr(TerminalRepr):
- def __init__(self, filename, firstlineno, deflines, errorstring):
- self.deflines = deflines
- self.errorstring = errorstring
- self.filename = filename
- self.firstlineno = firstlineno
-
- def toterminal(self, tw):
- tw.line()
- for line in self.deflines:
- tw.line(" " + line.strip())
- for line in self.errorstring.split("\n"):
- tw.line(" " + line.strip(), red=True)
- tw.line()
- tw.line("%s:%d" % (self.filename, self.firstlineno+1))
-
-
-class Generator(FunctionMixin, PyCollectorMixin, pytest.Collector):
- def collect(self):
- # test generators are seen as collectors but they also
- # invoke setup/teardown on popular request
- # (induced by the common "test_*" naming shared with normal tests)
- self.session._setupstate.prepare(self)
- # see FunctionMixin.setup and test_setupstate_is_preserved_134
- self._preservedparent = self.parent.obj
- l = []
- seen = {}
- for i, x in enumerate(self.obj()):
- name, call, args = self.getcallargs(x)
- if not py.builtin.callable(call):
- raise TypeError("%r yielded non callable test %r" %(self.obj, call,))
- if name is None:
- name = "[%d]" % i
- else:
- name = "['%s']" % name
- if name in seen:
- raise ValueError("%r generated tests with non-unique name %r" %(self, name))
- seen[name] = True
- l.append(self.Function(name, self, args=args, callobj=call))
- return l
-
- def getcallargs(self, obj):
- if not isinstance(obj, (tuple, list)):
- obj = (obj,)
- # explict naming
- if isinstance(obj[0], py.builtin._basestring):
- name = obj[0]
- obj = obj[1:]
- else:
- name = None
- call, args = obj[0], obj[1:]
- return name, call, args
-
-
-#
-# Test Items
-#
-_dummy = object()
-class Function(FunctionMixin, pytest.Item):
- """ a Function Item is responsible for setting up
- and executing a Python callable test object.
- """
- _genid = None
- def __init__(self, name, parent=None, args=None, config=None,
- callspec=None, callobj=_dummy, keywords=None, session=None):
- super(Function, self).__init__(name, parent,
- config=config, session=session)
- self._args = args
- if self._isyieldedfunction():
- assert not callspec, (
- "yielded functions (deprecated) cannot have funcargs")
- else:
- if callspec is not None:
- self.callspec = callspec
- self.funcargs = callspec.funcargs or {}
- self._genid = callspec.id
- if hasattr(callspec, "param"):
- self._requestparam = callspec.param
- else:
- self.funcargs = {}
- if callobj is not _dummy:
- self._obj = callobj
- self.function = getattr(self.obj, 'im_func', self.obj)
- self.keywords.update(py.builtin._getfuncdict(self.obj) or {})
- if keywords:
- self.keywords.update(keywords)
-
- def _getobj(self):
- name = self.name
- i = name.find("[") # parametrization
- if i != -1:
- name = name[:i]
- return getattr(self.parent.obj, name)
-
- def _isyieldedfunction(self):
- return self._args is not None
-
- def runtest(self):
- """ execute the underlying test function. """
- self.ihook.pytest_pyfunc_call(pyfuncitem=self)
-
- def setup(self):
- super(Function, self).setup()
- if hasattr(self, 'funcargs'):
- fillfuncargs(self)
-
- def __eq__(self, other):
- try:
- return (self.name == other.name and
- self._args == other._args and
- self.parent == other.parent and
- self.obj == other.obj and
- getattr(self, '_genid', None) ==
- getattr(other, '_genid', None)
- )
- except AttributeError:
- pass
- return False
-
- def __ne__(self, other):
- return not self == other
-
- def __hash__(self):
- return hash((self.parent, self.name))
-
-def hasinit(obj):
- init = getattr(obj, '__init__', None)
- if init:
- if init != object.__init__:
- return True
-
-
-def getfuncargnames(function, startindex=None):
- # XXX merge with main.py's varnames
- argnames = py.std.inspect.getargs(py.code.getrawcode(function))[0]
- if startindex is None:
- startindex = py.std.inspect.ismethod(function) and 1 or 0
- defaults = getattr(function, 'func_defaults',
- getattr(function, '__defaults__', None)) or ()
- numdefaults = len(defaults)
- if numdefaults:
- return argnames[startindex:-numdefaults]
- return argnames[startindex:]
-
-def fillfuncargs(function):
- """ fill missing funcargs. """
- request = FuncargRequest(pyfuncitem=function)
- request._fillfuncargs()
-
-_notexists = object()
-
-class CallSpec2(object):
- def __init__(self, metafunc):
- self.metafunc = metafunc
- self.funcargs = {}
- self._idlist = []
- self.params = {}
- self._globalid = _notexists
- self._globalid_args = set()
- self._globalparam = _notexists
-
- def copy(self, metafunc):
- cs = CallSpec2(self.metafunc)
- cs.funcargs.update(self.funcargs)
- cs.params.update(self.params)
- cs._idlist = list(self._idlist)
- cs._globalid = self._globalid
- cs._globalid_args = self._globalid_args
- cs._globalparam = self._globalparam
- return cs
-
- def _checkargnotcontained(self, arg):
- if arg in self.params or arg in self.funcargs:
- raise ValueError("duplicate %r" %(arg,))
-
- def getparam(self, name):
- try:
- return self.params[name]
- except KeyError:
- if self._globalparam is _notexists:
- raise ValueError(name)
- return self._globalparam
-
- @property
- def id(self):
- return "-".join(map(str, filter(None, self._idlist)))
-
- def setmulti(self, valtype, argnames, valset, id):
- for arg,val in zip(argnames, valset):
- self._checkargnotcontained(arg)
- getattr(self, valtype)[arg] = val
- self._idlist.append(id)
-
- def setall(self, funcargs, id, param):
- for x in funcargs:
- self._checkargnotcontained(x)
- self.funcargs.update(funcargs)
- if id is not _notexists:
- self._idlist.append(id)
- if param is not _notexists:
- assert self._globalparam is _notexists
- self._globalparam = param
-
-
-class Metafunc:
- def __init__(self, function, config=None, cls=None, module=None):
- self.config = config
- self.module = module
- self.function = function
- self.funcargnames = getfuncargnames(function,
- startindex=int(cls is not None))
- self.cls = cls
- self.module = module
- self._calls = []
- self._ids = py.builtin.set()
-
- def parametrize(self, argnames, argvalues, indirect=False, ids=None):
- """ Add new invocations to the underlying test function using the list
- of argvalues for the given argnames. Parametrization is performed
- during the collection phase. If you need to setup expensive resources
- you may pass indirect=True and implement a funcarg factory which can
- perform the expensive setup just before a test is actually run.
-
- :arg argnames: an argument name or a list of argument names
-
- :arg argvalues: a list of values for the argname or a list of tuples of
- values for the list of argument names.
-
- :arg indirect: if True each argvalue corresponding to an argument will
- be passed as request.param to its respective funcarg factory so
- that it can perform more expensive setups during the setup phase of
- a test rather than at collection time.
-
- :arg ids: list of string ids each corresponding to the argvalues so
- that they are part of the test id. If no ids are provided they will
- be generated automatically from the argvalues.
- """
- if not isinstance(argnames, (tuple, list)):
- argnames = (argnames,)
- argvalues = [(val,) for val in argvalues]
- if not indirect:
- #XXX should we also check for the opposite case?
- for arg in argnames:
- if arg not in self.funcargnames:
- raise ValueError("%r has no argument %r" %(self.function, arg))
- valtype = indirect and "params" or "funcargs"
- if not ids:
- idmaker = IDMaker()
- ids = list(map(idmaker, argvalues))
- newcalls = []
- for callspec in self._calls or [CallSpec2(self)]:
- for i, valset in enumerate(argvalues):
- assert len(valset) == len(argnames)
- newcallspec = callspec.copy(self)
- newcallspec.setmulti(valtype, argnames, valset, ids[i])
- newcalls.append(newcallspec)
- self._calls = newcalls
-
- def addcall(self, funcargs=None, id=_notexists, param=_notexists):
- """ (deprecated, use parametrize) Add a new call to the underlying
- test function during the collection phase of a test run. Note that
- request.addcall() is called during the test collection phase prior and
- independently to actual test execution. You should only use addcall()
- if you need to specify multiple arguments of a test function.
-
- :arg funcargs: argument keyword dictionary used when invoking
- the test function.
-
- :arg id: used for reporting and identification purposes. If you
- don't supply an `id` an automatic unique id will be generated.
-
- :arg param: a parameter which will be exposed to a later funcarg factory
- invocation through the ``request.param`` attribute.
- """
- assert funcargs is None or isinstance(funcargs, dict)
- if funcargs is not None:
- for name in funcargs:
- if name not in self.funcargnames:
- pytest.fail("funcarg %r not used in this function." % name)
- else:
- funcargs = {}
- if id is None:
- raise ValueError("id=None not allowed")
- if id is _notexists:
- id = len(self._calls)
- id = str(id)
- if id in self._ids:
- raise ValueError("duplicate id %r" % id)
- self._ids.add(id)
-
- cs = CallSpec2(self)
- cs.setall(funcargs, id, param)
- self._calls.append(cs)
-
-class IDMaker:
- def __init__(self):
- self.counter = 0
- def __call__(self, valset):
- l = []
- for val in valset:
- if not isinstance(val, (int, str)):
- val = "."+str(self.counter)
- self.counter += 1
- l.append(str(val))
- return "-".join(l)
-
-class FuncargRequest:
- """ A request for function arguments from a test function.
-
- Note that there is an optional ``param`` attribute in case
- there was an invocation to metafunc.addcall(param=...).
- If no such call was done in a ``pytest_generate_tests``
- hook, the attribute will not be present.
- """
- _argprefix = "pytest_funcarg__"
- _argname = None
-
- class LookupError(LookupError):
- """ error on performing funcarg request. """
-
- def __init__(self, pyfuncitem):
- self._pyfuncitem = pyfuncitem
- if hasattr(pyfuncitem, '_requestparam'):
- self.param = pyfuncitem._requestparam
- extra = [obj for obj in (self.module, self.instance) if obj]
- self._plugins = pyfuncitem.getplugins() + extra
- self._funcargs = self._pyfuncitem.funcargs.copy()
- self._name2factory = {}
- self._currentarg = None
-
- @property
- def function(self):
- """ function object of the test invocation. """
- return self._pyfuncitem.obj
-
- @property
- def keywords(self):
- """ keywords of the test function item.
-
- .. versionadded:: 2.0
- """
- return self._pyfuncitem.keywords
-
- @property
- def module(self):
- """ module where the test function was collected. """
- return self._pyfuncitem.getparent(pytest.Module).obj
-
- @property
- def cls(self):
- """ class (can be None) where the test function was collected. """
- clscol = self._pyfuncitem.getparent(pytest.Class)
- if clscol:
- return clscol.obj
- @property
- def instance(self):
- """ instance (can be None) on which test function was collected. """
- return py.builtin._getimself(self.function)
-
- @property
- def config(self):
- """ the pytest config object associated with this request. """
- return self._pyfuncitem.config
-
- @property
- def fspath(self):
- """ 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):
- """ Apply a marker to a single test function invocation.
- This method is useful if you don't want to have a keyword/marker
- on all function invocations.
-
- :arg marker: a :py:class:`_pytest.mark.MarkDecorator` object
- created by a call to ``py.test.mark.NAME(...)``.
- """
- if not isinstance(marker, py.test.mark.XYZ.__class__):
- raise ValueError("%r is not a py.test.mark.* object")
- self._pyfuncitem.keywords[marker.markname] = marker
-
- def cached_setup(self, setup, teardown=None, scope="module", extrakey=None):
- """ Return a testing resource managed by ``setup`` &
- ``teardown`` calls. ``scope`` and ``extrakey`` determine when the
- ``teardown`` function will be called so that subsequent calls to
- ``setup`` would recreate the resource.
-
- :arg teardown: function receiving a previously setup resource.
- :arg setup: a no-argument function creating a resource.
- :arg scope: a string value out of ``function``, ``class``, ``module``
- or ``session`` indicating the caching lifecycle of the resource.
- :arg extrakey: added to internal caching key of (funcargname, scope).
- """
- if not hasattr(self.config, '_setupcache'):
- self.config._setupcache = {} # XXX weakref?
- cachekey = (self._currentarg, self._getscopeitem(scope), extrakey)
- cache = self.config._setupcache
- try:
- val = cache[cachekey]
- except KeyError:
- val = setup()
- cache[cachekey] = val
- if teardown is not None:
- def finalizer():
- del cache[cachekey]
- teardown(val)
- self._addfinalizer(finalizer, scope=scope)
- return val
-
- def getfuncargvalue(self, argname):
- """ Retrieve a function argument by name for this test
- function invocation. This allows one function argument factory
- to call another function argument factory. If there are two
- funcarg factories for the same test function argument the first
- factory may use ``getfuncargvalue`` to call the second one and
- do something additional with the resource.
- """
- try:
- return self._funcargs[argname]
- except KeyError:
- pass
- if argname not in self._name2factory:
- self._name2factory[argname] = self.config.pluginmanager.listattr(
- plugins=self._plugins,
- attrname=self._argprefix + str(argname)
- )
- #else: we are called recursively
- if not self._name2factory[argname]:
- self._raiselookupfailed(argname)
- funcargfactory = self._name2factory[argname].pop()
- oldarg = self._currentarg
- mp = monkeypatch()
- mp.setattr(self, '_currentarg', argname)
- try:
- param = self._pyfuncitem.callspec.getparam(argname)
- except (AttributeError, ValueError):
- pass
- else:
- mp.setattr(self, 'param', param, raising=False)
- try:
- self._funcargs[argname] = res = funcargfactory(request=self)
- finally:
- mp.undo()
- return res
-
- def _getscopeitem(self, scope):
- if scope == "function":
- return self._pyfuncitem
- elif scope == "session":
- return None
- elif scope == "class":
- x = self._pyfuncitem.getparent(pytest.Class)
- if x is not None:
- return x
- scope = "module"
- if scope == "module":
- return self._pyfuncitem.getparent(pytest.Module)
- raise ValueError("unknown finalization scope %r" %(scope,))
-
- def addfinalizer(self, finalizer):
- """add finalizer function to be called after test function
- finished execution. """
- self._addfinalizer(finalizer, scope="function")
-
- def _addfinalizer(self, finalizer, scope):
- colitem = self._getscopeitem(scope)
- self._pyfuncitem.session._setupstate.addfinalizer(
- finalizer=finalizer, colitem=colitem)
-
- def __repr__(self):
- return "<FuncargRequest for %r>" %(self._pyfuncitem)
-
- def _raiselookupfailed(self, argname):
- available = []
- for plugin in self._plugins:
- for name in vars(plugin):
- if name.startswith(self._argprefix):
- name = name[len(self._argprefix):]
- if name not in available:
- available.append(name)
- fspath, lineno, msg = self._pyfuncitem.reportinfo()
- msg = "LookupError: no factory found for function argument %r" % (argname,)
- msg += "\n available funcargs: %s" %(", ".join(available),)
- msg += "\n use 'py.test --funcargs [testpath]' for help on them."
- raise self.LookupError(msg)
-
-def showfuncargs(config):
- from _pytest.main import wrap_session
- return wrap_session(config, _showfuncargs_main)
-
-def _showfuncargs_main(config, session):
- session.perform_collect()
- if session.items:
- plugins = session.items[0].getplugins()
- else:
- plugins = session.getplugins()
- curdir = py.path.local()
- tw = py.io.TerminalWriter()
- verbose = config.getvalue("verbose")
- for plugin in plugins:
- available = []
- for name, factory in vars(plugin).items():
- if name.startswith(FuncargRequest._argprefix):
- name = name[len(FuncargRequest._argprefix):]
- if name not in available:
- available.append([name, factory])
- if available:
- pluginname = plugin.__name__
- for name, factory in available:
- loc = getlocation(factory, curdir)
- if verbose:
- funcargspec = "%s -- %s" %(name, loc,)
- else:
- funcargspec = name
- tw.line(funcargspec, green=True)
- doc = factory.__doc__ or ""
- if doc:
- for line in doc.split("\n"):
- tw.line(" " + line.strip())
- else:
- tw.line(" %s: no docstring available" %(loc,),
- red=True)
-
-def getlocation(function, curdir):
- import inspect
- fn = py.path.local(inspect.getfile(function))
- lineno = py.builtin._getcode(function).co_firstlineno
- if fn.relto(curdir):
- fn = fn.relto(curdir)
- return "%s:%d" %(fn, lineno+1)
-
-# builtin pytest.raises helper
-
-def raises(ExpectedException, *args, **kwargs):
- """ assert that a code block/function call raises @ExpectedException
- and raise a failure exception otherwise.
-
- If using Python 2.5 or above, you may use this function as a
- context manager::
-
- >>> with raises(ZeroDivisionError):
- ... 1/0
-
- Or you can specify a callable by passing a to-be-called lambda::
-
- >>> raises(ZeroDivisionError, lambda: 1/0)
- <ExceptionInfo ...>
-
- or you can specify an arbitrary callable with arguments::
-
- >>> def f(x): return 1/x
- ...
- >>> raises(ZeroDivisionError, f, 0)
- <ExceptionInfo ...>
- >>> raises(ZeroDivisionError, f, x=0)
- <ExceptionInfo ...>
-
- A third possibility is to use a string to be executed::
-
- >>> raises(ZeroDivisionError, "f(0)")
- <ExceptionInfo ...>
- """
- __tracebackhide__ = True
-
- if not args:
- return RaisesContext(ExpectedException)
- elif isinstance(args[0], str):
- code, = args
- assert isinstance(code, str)
- frame = sys._getframe(1)
- loc = frame.f_locals.copy()
- loc.update(kwargs)
- #print "raises frame scope: %r" % frame.f_locals
- try:
- code = py.code.Source(code).compile()
- py.builtin.exec_(code, frame.f_globals, loc)
- # XXX didn'T mean f_globals == f_locals something special?
- # this is destroyed here ...
- except ExpectedException:
- return py.code.ExceptionInfo()
- else:
- func = args[0]
- try:
- func(*args[1:], **kwargs)
- except ExpectedException:
- return py.code.ExceptionInfo()
- k = ", ".join(["%s=%r" % x for x in kwargs.items()])
- if k:
- k = ', ' + k
- expr = '%s(%r%s)' %(getattr(func, '__name__', func), args, k)
- pytest.fail("DID NOT RAISE")
-
-class RaisesContext(object):
- def __init__(self, ExpectedException):
- self.ExpectedException = ExpectedException
- self.excinfo = None
-
- def __enter__(self):
- self.excinfo = object.__new__(py.code.ExceptionInfo)
- return self.excinfo
-
- def __exit__(self, *tp):
- __tracebackhide__ = True
- if tp[0] is None:
- pytest.fail("DID NOT RAISE")
- self.excinfo.__init__(tp)
- return issubclass(self.excinfo.type, self.ExpectedException)
-
diff --git a/_pytest/recwarn.py b/_pytest/recwarn.py
deleted file mode 100644
--- a/_pytest/recwarn.py
+++ /dev/null
@@ -1,99 +0,0 @@
-""" recording warnings during test function execution. """
-
-import py
-import sys, os
-
-def pytest_funcarg__recwarn(request):
- """Return a WarningsRecorder instance that provides these methods:
-
- * ``pop(category=None)``: return last warning matching the category.
- * ``clear()``: clear list of warnings
-
- See http://docs.python.org/library/warnings.html for information
- on warning categories.
- """
- if sys.version_info >= (2,7):
- import warnings
- oldfilters = warnings.filters[:]
- warnings.simplefilter('default')
- def reset_filters():
- warnings.filters[:] = oldfilters
- request.addfinalizer(reset_filters)
- wrec = WarningsRecorder()
- request.addfinalizer(wrec.finalize)
- return wrec
-
-def pytest_namespace():
- return {'deprecated_call': deprecated_call}
-
-def deprecated_call(func, *args, **kwargs):
- """ assert that calling ``func(*args, **kwargs)``
- triggers a DeprecationWarning.
- """
- warningmodule = py.std.warnings
- l = []
- oldwarn_explicit = getattr(warningmodule, 'warn_explicit')
- def warn_explicit(*args, **kwargs):
- l.append(args)
- oldwarn_explicit(*args, **kwargs)
- oldwarn = getattr(warningmodule, 'warn')
- def warn(*args, **kwargs):
- l.append(args)
- oldwarn(*args, **kwargs)
-
- warningmodule.warn_explicit = warn_explicit
- warningmodule.warn = warn
- try:
- ret = func(*args, **kwargs)
- finally:
- warningmodule.warn_explicit = warn_explicit
- warningmodule.warn = warn
- if not l:
- #print warningmodule
- __tracebackhide__ = True
- raise AssertionError("%r did not produce DeprecationWarning" %(func,))
- return ret
-
-
-class RecordedWarning:
- def __init__(self, message, category, filename, lineno, line):
- self.message = message
- self.category = category
- self.filename = filename
- self.lineno = lineno
- self.line = line
-
-class WarningsRecorder:
- def __init__(self):
- warningmodule = py.std.warnings
- self.list = []
- def showwarning(message, category, filename, lineno, line=0):
- self.list.append(RecordedWarning(
- message, category, filename, lineno, line))
- try:
- self.old_showwarning(message, category,
- filename, lineno, line=line)
- except TypeError:
- # < python2.6
- self.old_showwarning(message, category, filename, lineno)
- self.old_showwarning = warningmodule.showwarning
- warningmodule.showwarning = showwarning
-
- def pop(self, cls=Warning):
- """ pop the first recorded warning, raise exception if not exists."""
- for i, w in enumerate(self.list):
- if issubclass(w.category, cls):
- return self.list.pop(i)
- __tracebackhide__ = True
- assert 0, "%r not found in %r" %(cls, self.list)
-
- #def resetregistry(self):
- # import warnings
- # warnings.onceregistry.clear()
- # warnings.__warningregistry__.clear()
-
- def clear(self):
- self.list[:] = []
-
- def finalize(self):
- py.std.warnings.showwarning = self.old_showwarning
diff --git a/_pytest/resultlog.py b/_pytest/resultlog.py
deleted file mode 100644
--- a/_pytest/resultlog.py
+++ /dev/null
@@ -1,98 +0,0 @@
-""" (disabled by default) create result information in a plain text file. """
-
-import py
-
-def pytest_addoption(parser):
- group = parser.getgroup("terminal reporting", "resultlog plugin options")
- group.addoption('--resultlog', action="store", dest="resultlog",
- metavar="path", default=None,
- help="path for machine-readable result log.")
-
-def pytest_configure(config):
- resultlog = config.option.resultlog
- # prevent opening resultlog on slave nodes (xdist)
- if resultlog and not hasattr(config, 'slaveinput'):
- logfile = open(resultlog, 'w', 1) # line buffered
- config._resultlog = ResultLog(config, logfile)
- config.pluginmanager.register(config._resultlog)
-
-def pytest_unconfigure(config):
- resultlog = getattr(config, '_resultlog', None)
- if resultlog:
- resultlog.logfile.close()
- del config._resultlog
- config.pluginmanager.unregister(resultlog)
-
-def generic_path(item):
- chain = item.listchain()
- gpath = [chain[0].name]
- fspath = chain[0].fspath
- fspart = False
- for node in chain[1:]:
- newfspath = node.fspath
- if newfspath == fspath:
- if fspart:
- gpath.append(':')
- fspart = False
- else:
- gpath.append('.')
- else:
- gpath.append('/')
- fspart = True
- name = node.name
- if name[0] in '([':
- gpath.pop()
- gpath.append(name)
- fspath = newfspath
- return ''.join(gpath)
-
-class ResultLog(object):
- def __init__(self, config, logfile):
- self.config = config
- self.logfile = logfile # preferably line buffered
-
- def write_log_entry(self, testpath, lettercode, longrepr):
- py.builtin.print_("%s %s" % (lettercode, testpath), file=self.logfile)
- for line in longrepr.splitlines():
- py.builtin.print_(" %s" % line, file=self.logfile)
-
- def log_outcome(self, report, lettercode, longrepr):
- testpath = getattr(report, 'nodeid', None)
- if testpath is None:
- testpath = report.fspath
- self.write_log_entry(testpath, lettercode, longrepr)
-
- def pytest_runtest_logreport(self, report):
- if report.when != "call" and report.passed:
- return
- res = self.config.hook.pytest_report_teststatus(report=report)
- code = res[1]
- if code == 'x':
- longrepr = str(report.longrepr)
- elif code == 'X':
- longrepr = ''
- elif report.passed:
- longrepr = ""
- elif report.failed:
- longrepr = str(report.longrepr)
- elif report.skipped:
- longrepr = str(report.longrepr[2])
- self.log_outcome(report, code, longrepr)
-
- def pytest_collectreport(self, report):
- if not report.passed:
- if report.failed:
- code = "F"
- longrepr = str(report.longrepr.reprcrash)
- else:
- assert report.skipped
- code = "S"
- longrepr = "%s:%d: %s" % report.longrepr
- self.log_outcome(report, code, longrepr)
-
- def pytest_internalerror(self, excrepr):
- reprcrash = getattr(excrepr, 'reprcrash', None)
- path = getattr(reprcrash, "path", None)
- if path is None:
- path = "cwd:%s" % py.path.local()
- self.write_log_entry(path, '!', str(excrepr))
diff --git a/_pytest/runner.py b/_pytest/runner.py
deleted file mode 100644
--- a/_pytest/runner.py
+++ /dev/null
@@ -1,432 +0,0 @@
-""" basic collect and runtest protocol implementations """
-
-import py, sys, time
-from py._code.code import TerminalRepr
-
-def pytest_namespace():
- return {
- 'fail' : fail,
- 'skip' : skip,
- 'importorskip' : importorskip,
- 'exit' : exit,
- }
-
-#
-# pytest plugin hooks
-
-def pytest_addoption(parser):
- group = parser.getgroup("terminal reporting", "reporting", after="general")
- group.addoption('--durations',
- action="store", type="int", default=None, metavar="N",
- help="show N slowest setup/test durations (N=0 for all)."),
-
-def pytest_terminal_summary(terminalreporter):
- durations = terminalreporter.config.option.durations
- if durations is None:
- return
- tr = terminalreporter
- dlist = []
- for replist in tr.stats.values():
- for rep in replist:
- if hasattr(rep, 'duration'):
- dlist.append(rep)
- if not dlist:
- return
- dlist.sort(key=lambda x: x.duration)
- dlist.reverse()
- if not durations:
- tr.write_sep("=", "slowest test durations")
- else:
- tr.write_sep("=", "slowest %s test durations" % durations)
- dlist = dlist[:durations]
-
- for rep in dlist:
- nodeid = rep.nodeid.replace("::()::", "::")
- tr.write_line("%02.2fs %-8s %s" %
- (rep.duration, rep.when, nodeid))
-
-def pytest_sessionstart(session):
- session._setupstate = SetupState()
-def pytest_sessionfinish(session):
- session._setupstate.teardown_all()
-
-class NodeInfo:
- def __init__(self, location):
- self.location = location
-
-def pytest_runtest_protocol(item, nextitem):
- item.ihook.pytest_runtest_logstart(
- nodeid=item.nodeid, location=item.location,
- )
- runtestprotocol(item, nextitem=nextitem)
- return True
-
-def runtestprotocol(item, log=True, nextitem=None):
- rep = call_and_report(item, "setup", log)
- reports = [rep]
- if rep.passed:
- reports.append(call_and_report(item, "call", log))
- reports.append(call_and_report(item, "teardown", log,
- nextitem=nextitem))
- return reports
-
-def pytest_runtest_setup(item):
- item.session._setupstate.prepare(item)
-
-def pytest_runtest_call(item):
- item.runtest()
-
-def pytest_runtest_teardown(item, nextitem):
- item.session._setupstate.teardown_exact(item, nextitem)
-
-def pytest_report_teststatus(report):
- if report.when in ("setup", "teardown"):
- if report.failed:
- # category, shortletter, verbose-word
- return "error", "E", "ERROR"
- elif report.skipped:
- return "skipped", "s", "SKIPPED"
- else:
- return "", "", ""
-
-
-#
-# Implementation
-
-def call_and_report(item, when, log=True, **kwds):
- call = call_runtest_hook(item, when, **kwds)
- hook = item.ihook
- report = hook.pytest_runtest_makereport(item=item, call=call)
- if log:
- hook.pytest_runtest_logreport(report=report)
- return report
-
-def call_runtest_hook(item, when, **kwds):
- hookname = "pytest_runtest_" + when
- ihook = getattr(item.ihook, hookname)
- return CallInfo(lambda: ihook(item=item, **kwds), when=when)
-
-class CallInfo:
- """ Result/Exception info a function invocation. """
- #: None or ExceptionInfo object.
- excinfo = None
- def __init__(self, func, when):
- #: context of invocation: one of "setup", "call",
- #: "teardown", "memocollect"
- self.when = when
- self.start = time.time()
- try:
- try:
- self.result = func()
- except KeyboardInterrupt:
- raise
- except:
- self.excinfo = py.code.ExceptionInfo()
- finally:
- self.stop = time.time()
-
- def __repr__(self):
- if self.excinfo:
- status = "exception: %s" % str(self.excinfo.value)
- else:
- status = "result: %r" % (self.result,)
- return "<CallInfo when=%r %s>" % (self.when, status)
-
-def getslaveinfoline(node):
- try:
- return node._slaveinfocache
- except AttributeError:
- d = node.slaveinfo
- ver = "%s.%s.%s" % d['version_info'][:3]
- node._slaveinfocache = s = "[%s] %s -- Python %s %s" % (
- d['id'], d['sysplatform'], ver, d['executable'])
- return s
-
-class BaseReport(object):
-
- def __init__(self, **kw):
- self.__dict__.update(kw)
-
- def toterminal(self, out):
- longrepr = self.longrepr
- if hasattr(self, 'node'):
- out.line(getslaveinfoline(self.node))
- if hasattr(longrepr, 'toterminal'):
- longrepr.toterminal(out)
- else:
- out.line(str(longrepr))
-
- passed = property(lambda x: x.outcome == "passed")
- failed = property(lambda x: x.outcome == "failed")
- skipped = property(lambda x: x.outcome == "skipped")
-
- @property
- def fspath(self):
- return self.nodeid.split("::")[0]
-
-def pytest_runtest_makereport(item, call):
- when = call.when
- duration = call.stop-call.start
- keywords = dict([(x,1) for x in item.keywords])
- excinfo = call.excinfo
- if not call.excinfo:
- outcome = "passed"
- longrepr = None
- else:
- excinfo = call.excinfo
- if not isinstance(excinfo, py.code.ExceptionInfo):
- outcome = "failed"
- longrepr = excinfo
- elif excinfo.errisinstance(py.test.skip.Exception):
- outcome = "skipped"
- r = excinfo._getreprcrash()
- longrepr = (str(r.path), r.lineno, r.message)
- else:
- outcome = "failed"
- if call.when == "call":
- longrepr = item.repr_failure(excinfo)
- else: # exception in setup or teardown
- longrepr = item._repr_failure_py(excinfo)
- return TestReport(item.nodeid, item.location,
- keywords, outcome, longrepr, when,
- duration=duration)
-
-class TestReport(BaseReport):
- """ Basic test report object (also used for setup and teardown calls if
- they fail).
- """
- def __init__(self, nodeid, location,
- keywords, outcome, longrepr, when, sections=(), duration=0, **extra):
- #: normalized collection node id
- self.nodeid = nodeid
-
- #: a (filesystempath, lineno, domaininfo) tuple indicating the
- #: actual location of a test item - it might be different from the
- #: collected one e.g. if a method is inherited from a different module.
- self.location = location
-
- #: a name -> value dictionary containing all keywords and
- #: markers associated with a test invocation.
- self.keywords = keywords
-
- #: test outcome, always one of "passed", "failed", "skipped".
- self.outcome = outcome
-
- #: None or a failure representation.
- self.longrepr = longrepr
-
- #: one of 'setup', 'call', 'teardown' to indicate runtest phase.
- self.when = when
-
- #: list of (secname, data) extra information which needs to
- #: marshallable
- self.sections = list(sections)
-
- #: time it took to run just the test
- self.duration = duration
-
- self.__dict__.update(extra)
-
- def __repr__(self):
- return "<TestReport %r when=%r outcome=%r>" % (
- self.nodeid, self.when, self.outcome)
-
-class TeardownErrorReport(BaseReport):
- outcome = "failed"
- when = "teardown"
- def __init__(self, longrepr, **extra):
- self.longrepr = longrepr
- self.sections = []
- self.__dict__.update(extra)
-
-def pytest_make_collect_report(collector):
- call = CallInfo(collector._memocollect, "memocollect")
- longrepr = None
- if not call.excinfo:
- outcome = "passed"
- else:
- if call.excinfo.errisinstance(py.test.skip.Exception):
- outcome = "skipped"
- r = collector._repr_failure_py(call.excinfo, "line").reprcrash
- longrepr = (str(r.path), r.lineno, r.message)
- else:
- outcome = "failed"
- errorinfo = collector.repr_failure(call.excinfo)
- if not hasattr(errorinfo, "toterminal"):
- errorinfo = CollectErrorRepr(errorinfo)
- longrepr = errorinfo
- return CollectReport(collector.nodeid, outcome, longrepr,
- getattr(call, 'result', None))
-
-class CollectReport(BaseReport):
- def __init__(self, nodeid, outcome, longrepr, result, sections=(), **extra):
- self.nodeid = nodeid
- self.outcome = outcome
- self.longrepr = longrepr
- self.result = result or []
- self.sections = list(sections)
- self.__dict__.update(extra)
-
- @property
- def location(self):
- return (self.fspath, None, self.fspath)
-
- def __repr__(self):
- return "<CollectReport %r lenresult=%s outcome=%r>" % (
- self.nodeid, len(self.result), self.outcome)
-
-class CollectErrorRepr(TerminalRepr):
- def __init__(self, msg):
- self.longrepr = msg
- def toterminal(self, out):
- out.line(str(self.longrepr), red=True)
-
-class SetupState(object):
- """ shared state for setting up/tearing down test items or collectors. """
- def __init__(self):
- self.stack = []
- self._finalizers = {}
-
- def addfinalizer(self, finalizer, colitem):
- """ attach a finalizer to the given colitem.
- if colitem is None, this will add a finalizer that
- is called at the end of teardown_all().
- """
- assert hasattr(finalizer, '__call__')
- #assert colitem in self.stack
- self._finalizers.setdefault(colitem, []).append(finalizer)
-
- def _pop_and_teardown(self):
- colitem = self.stack.pop()
- self._teardown_with_finalization(colitem)
-
- def _callfinalizers(self, colitem):
- finalizers = self._finalizers.pop(colitem, None)
- while finalizers:
- fin = finalizers.pop()
- fin()
-
- def _teardown_with_finalization(self, colitem):
- self._callfinalizers(colitem)
- if colitem:
- colitem.teardown()
- for colitem in self._finalizers:
- assert colitem is None or colitem in self.stack
-
- def teardown_all(self):
- while self.stack:
- self._pop_and_teardown()
- self._teardown_with_finalization(None)
- assert not self._finalizers
-
- def teardown_exact(self, item, nextitem):
- needed_collectors = nextitem and nextitem.listchain() or []
- self._teardown_towards(needed_collectors)
-
- def _teardown_towards(self, needed_collectors):
- while self.stack:
- if self.stack == needed_collectors[:len(self.stack)]:
- break
- self._pop_and_teardown()
-
- def prepare(self, colitem):
- """ setup objects along the collector chain to the test-method
- and teardown previously setup objects."""
- needed_collectors = colitem.listchain()
- self._teardown_towards(needed_collectors)
-
- # check if the last collection node has raised an error
- for col in self.stack:
- if hasattr(col, '_prepare_exc'):
- py.builtin._reraise(*col._prepare_exc)
- for col in needed_collectors[len(self.stack):]:
- self.stack.append(col)
- try:
- col.setup()
- except Exception:
- col._prepare_exc = sys.exc_info()
- raise
-
-# =============================================================
-# Test OutcomeExceptions and helpers for creating them.
-
-
-class OutcomeException(Exception):
- """ OutcomeException and its subclass instances indicate and
- contain info about test and collection outcomes.
- """
- def __init__(self, msg=None, pytrace=True):
- self.msg = msg
- self.pytrace = pytrace
-
- def __repr__(self):
- if self.msg:
- return str(self.msg)
- return "<%s instance>" %(self.__class__.__name__,)
- __str__ = __repr__
-
-class Skipped(OutcomeException):
- # XXX hackish: on 3k we fake to live in the builtins
- # in order to have Skipped exception printing shorter/nicer
- __module__ = 'builtins'
-
-class Failed(OutcomeException):
- """ raised from an explicit call to py.test.fail() """
- __module__ = 'builtins'
-
-class Exit(KeyboardInterrupt):
- """ raised for immediate program exits (no tracebacks/summaries)"""
- def __init__(self, msg="unknown reason"):
- self.msg = msg
- KeyboardInterrupt.__init__(self, msg)
-
-# exposed helper methods
-
-def exit(msg):
- """ exit testing process as if KeyboardInterrupt was triggered. """
- __tracebackhide__ = True
- raise Exit(msg)
-
-exit.Exception = Exit
-
-def skip(msg=""):
- """ skip an executing test with the given message. Note: it's usually
- better to use the py.test.mark.skipif marker to declare a test to be
- skipped under certain conditions like mismatching platforms or
- dependencies. See the pytest_skipping plugin for details.
- """
- __tracebackhide__ = True
- raise Skipped(msg=msg)
-skip.Exception = Skipped
-
-def fail(msg="", pytrace=True):
- """ explicitely fail an currently-executing test with the given Message.
- if @pytrace is not True the msg represents the full failure information.
- """
- __tracebackhide__ = True
- raise Failed(msg=msg, pytrace=pytrace)
-fail.Exception = Failed
-
-
-def importorskip(modname, minversion=None):
- """ return imported module if it has a higher __version__ than the
- optionally specified 'minversion' - otherwise call py.test.skip()
- with a message detailing the mismatch.
- """
- __tracebackhide__ = True
- compile(modname, '', 'eval') # to catch syntaxerrors
- try:
- mod = __import__(modname, None, None, ['__doc__'])
- except ImportError:
- py.test.skip("could not import %r" %(modname,))
- if minversion is None:
- return mod
- verattr = getattr(mod, '__version__', None)
- if isinstance(minversion, str):
- minver = minversion.split(".")
- else:
- minver = list(minversion)
- if verattr is None or verattr.split(".") < minver:
- py.test.skip("module %r has __version__ %r, required is: %r" %(
- modname, verattr, minversion))
- return mod
diff --git a/_pytest/skipping.py b/_pytest/skipping.py
deleted file mode 100644
--- a/_pytest/skipping.py
+++ /dev/null
@@ -1,271 +0,0 @@
-""" support for skip/xfail functions and markers. """
-
-import py, pytest
-import sys
-
-def pytest_addoption(parser):
- group = parser.getgroup("general")
- group.addoption('--runxfail',
- action="store_true", dest="runxfail", default=False,
- help="run tests even if they are marked xfail")
-
-def pytest_configure(config):
- config.addinivalue_line("markers",
- "skipif(*conditions): skip the given test function if evaluation "
- "of all conditions has a True value. Evaluation happens within the "
- "module global context. Example: skipif('sys.platform == \"win32\"') "
- "skips the test if we are on the win32 platform. "
- )
- config.addinivalue_line("markers",
- "xfail(*conditions, reason=None, run=True): mark the the test function "
- "as an expected failure. Optionally specify a reason and run=False "
- "if you don't even want to execute the test function. Any positional "
- "condition strings will be evaluated (like with skipif) and if one is "
- "False the marker will not be applied."
- )
-
-def pytest_namespace():
- return dict(xfail=xfail)
-
-class XFailed(pytest.fail.Exception):
- """ raised from an explicit call to py.test.xfail() """
-
-def xfail(reason=""):
- """ xfail an executing test or setup functions with the given reason."""
- __tracebackhide__ = True
- raise XFailed(reason)
-xfail.Exception = XFailed
-
-class MarkEvaluator:
- def __init__(self, item, name):
- self.item = item
- self.name = name
-
- @property
- def holder(self):
- return self.item.keywords.get(self.name, None)
- def __bool__(self):
- return bool(self.holder)
- __nonzero__ = __bool__
-
- def wasvalid(self):
- return not hasattr(self, 'exc')
-
- def istrue(self):
- try:
- return self._istrue()
- except KeyboardInterrupt:
- raise
- except:
- self.exc = sys.exc_info()
- if isinstance(self.exc[1], SyntaxError):
- msg = [" " * (self.exc[1].offset + 4) + "^",]
- msg.append("SyntaxError: invalid syntax")
- else:
- msg = py.std.traceback.format_exception_only(*self.exc[:2])
- pytest.fail("Error evaluating %r expression\n"
- " %s\n"
- "%s"
- %(self.name, self.expr, "\n".join(msg)),
- pytrace=False)
-
- def _getglobals(self):
- d = {'os': py.std.os, 'sys': py.std.sys, 'config': self.item.config}
- func = self.item.obj
- try:
- d.update(func.__globals__)
- except AttributeError:
- d.update(func.func_globals)
- return d
-
- def _istrue(self):
- if self.holder:
- d = self._getglobals()
- if self.holder.args:
- self.result = False
- for expr in self.holder.args:
- self.expr = expr
- if isinstance(expr, str):
- result = cached_eval(self.item.config, expr, d)
- else:
- pytest.fail("expression is not a string")
- if result:
- self.result = True
- self.expr = expr
- break
- else:
- self.result = True
- return getattr(self, 'result', False)
-
- def get(self, attr, default=None):
- return self.holder.kwargs.get(attr, default)
-
- def getexplanation(self):
- expl = self.get('reason', None)
- if not expl:
- if not hasattr(self, 'expr'):
- return ""
- else:
- return "condition: " + str(self.expr)
- return expl
-
-
-def pytest_runtest_setup(item):
- if not isinstance(item, pytest.Function):
- return
- evalskip = MarkEvaluator(item, 'skipif')
- if evalskip.istrue():
- py.test.skip(evalskip.getexplanation())
- item._evalxfail = MarkEvaluator(item, 'xfail')
- check_xfail_no_run(item)
-
-def pytest_pyfunc_call(pyfuncitem):
- check_xfail_no_run(pyfuncitem)
-
-def check_xfail_no_run(item):
- if not item.config.option.runxfail:
- evalxfail = item._evalxfail
- if evalxfail.istrue():
- if not evalxfail.get('run', True):
- py.test.xfail("[NOTRUN] " + evalxfail.getexplanation())
-
-def pytest_runtest_makereport(__multicall__, item, call):
- if not isinstance(item, pytest.Function):
- return
- # unitttest special case, see setting of _unexpectedsuccess
- if hasattr(item, '_unexpectedsuccess'):
- rep = __multicall__.execute()
- if rep.when == "call":
- # we need to translate into how py.test encodes xpass
- rep.keywords['xfail'] = "reason: " + item._unexpectedsuccess
- rep.outcome = "failed"
- return rep
- if not (call.excinfo and
- call.excinfo.errisinstance(py.test.xfail.Exception)):
- evalxfail = getattr(item, '_evalxfail', None)
- if not evalxfail:
- return
- if call.excinfo and call.excinfo.errisinstance(py.test.xfail.Exception):
- if not item.config.getvalue("runxfail"):
- rep = __multicall__.execute()
- rep.keywords['xfail'] = "reason: " + call.excinfo.value.msg
- rep.outcome = "skipped"
- return rep
- rep = __multicall__.execute()
- evalxfail = item._evalxfail
- if not item.config.option.runxfail:
- if evalxfail.wasvalid() and evalxfail.istrue():
- if call.excinfo:
- rep.outcome = "skipped"
- rep.keywords['xfail'] = evalxfail.getexplanation()
- elif call.when == "call":
- rep.outcome = "failed"
- rep.keywords['xfail'] = evalxfail.getexplanation()
- return rep
- if 'xfail' in rep.keywords:
- del rep.keywords['xfail']
- return rep
-
-# called by terminalreporter progress reporting
-def pytest_report_teststatus(report):
- if 'xfail' in report.keywords:
- if report.skipped:
- return "xfailed", "x", "xfail"
- elif report.failed:
- return "xpassed", "X", "XPASS"
-
-# called by the terminalreporter instance/plugin
-def pytest_terminal_summary(terminalreporter):
- tr = terminalreporter
- if not tr.reportchars:
- #for name in "xfailed skipped failed xpassed":
- # if not tr.stats.get(name, 0):
- # tr.write_line("HINT: use '-r' option to see extra "
- # "summary info about tests")
- # break
- return
-
- lines = []
- for char in tr.reportchars:
- if char == "x":
- show_xfailed(terminalreporter, lines)
- elif char == "X":
- show_xpassed(terminalreporter, lines)
- elif char in "fF":
- show_simple(terminalreporter, lines, 'failed', "FAIL %s")
- elif char in "sS":
- show_skipped(terminalreporter, lines)
- elif char == "E":
- show_simple(terminalreporter, lines, 'error', "ERROR %s")
- if lines:
- tr._tw.sep("=", "short test summary info")
- for line in lines:
- tr._tw.line(line)
-
-def show_simple(terminalreporter, lines, stat, format):
- tw = terminalreporter._tw
- failed = terminalreporter.stats.get(stat)
- if failed:
- for rep in failed:
- pos = rep.nodeid
- lines.append(format %(pos, ))
-
-def show_xfailed(terminalreporter, lines):
- xfailed = terminalreporter.stats.get("xfailed")
- if xfailed:
- for rep in xfailed:
- pos = rep.nodeid
- reason = rep.keywords['xfail']
- lines.append("XFAIL %s" % (pos,))
- if reason:
- lines.append(" " + str(reason))
-
-def show_xpassed(terminalreporter, lines):
- xpassed = terminalreporter.stats.get("xpassed")
- if xpassed:
- for rep in xpassed:
- pos = rep.nodeid
- reason = rep.keywords['xfail']
- lines.append("XPASS %s %s" %(pos, reason))
-
-def cached_eval(config, expr, d):
- if not hasattr(config, '_evalcache'):
- config._evalcache = {}
- try:
- return config._evalcache[expr]
- except KeyError:
- #import sys
- #print >>sys.stderr, ("cache-miss: %r" % expr)
- exprcode = py.code.compile(expr, mode="eval")
- config._evalcache[expr] = x = eval(exprcode, d)
- return x
-
-
-def folded_skips(skipped):
- d = {}
- for event in skipped:
- key = event.longrepr
- assert len(key) == 3, (event, key)
- d.setdefault(key, []).append(event)
- l = []
- for key, events in d.items():
- l.append((len(events),) + key)
- return l
-
-def show_skipped(terminalreporter, lines):
- tr = terminalreporter
- skipped = tr.stats.get('skipped', [])
- if skipped:
- #if not tr.hasopt('skipped'):
- # tr.write_line(
- # "%d skipped tests, specify -rs for more info" %
- # len(skipped))
- # return
- fskips = folded_skips(skipped)
- if fskips:
- #tr.write_sep("_", "skipped test summary")
- for num, fspath, lineno, reason in fskips:
- if reason.startswith("Skipped: "):
- reason = reason[9:]
- lines.append("SKIP [%d] %s:%d: %s" %
- (num, fspath, lineno, reason))
diff --git a/_pytest/standalonetemplate.py b/_pytest/standalonetemplate.py
deleted file mode 100755
--- a/_pytest/standalonetemplate.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#! /usr/bin/env python
-
-sources = """
- at SOURCES@"""
-
-import sys
-import base64
-import zlib
-import imp
-
-class DictImporter(object):
- def __init__(self, sources):
- self.sources = sources
-
- def find_module(self, fullname, path=None):
- if fullname in self.sources:
- return self
- if fullname + '.__init__' in self.sources:
- return self
- return None
-
- def load_module(self, fullname):
- # print "load_module:", fullname
- from types import ModuleType
- try:
- s = self.sources[fullname]
- is_pkg = False
- except KeyError:
- s = self.sources[fullname + '.__init__']
- is_pkg = True
-
- co = compile(s, fullname, 'exec')
- module = sys.modules.setdefault(fullname, ModuleType(fullname))
- module.__file__ = "%s/%s" % (__file__, fullname)
- module.__loader__ = self
- if is_pkg:
- module.__path__ = [fullname]
-
- do_exec(co, module.__dict__)
- return sys.modules[fullname]
-
- def get_source(self, name):
- res = self.sources.get(name)
- if res is None:
- res = self.sources.get(name + '.__init__')
- return res
-
-if __name__ == "__main__":
- if sys.version_info >= (3, 0):
- exec("def do_exec(co, loc): exec(co, loc)\n")
- import pickle
- sources = sources.encode("ascii") # ensure bytes
- sources = pickle.loads(zlib.decompress(base64.decodebytes(sources)))
- else:
- import cPickle as pickle
- exec("def do_exec(co, loc): exec co in loc\n")
- sources = pickle.loads(zlib.decompress(base64.decodestring(sources)))
-
- importer = DictImporter(sources)
- sys.meta_path.append(importer)
-
- entry = "@ENTRY@"
- do_exec(entry, locals())
diff --git a/_pytest/terminal.py b/_pytest/terminal.py
deleted file mode 100644
--- a/_pytest/terminal.py
+++ /dev/null
@@ -1,473 +0,0 @@
-""" terminal reporting of the full testing process.
-
-This is a good source for looking at the various reporting hooks.
-"""
-import pytest, py
-import sys
-import os
-
-def pytest_addoption(parser):
- group = parser.getgroup("terminal reporting", "reporting", after="general")
- group._addoption('-v', '--verbose', action="count",
- dest="verbose", default=0, help="increase verbosity."),
- group._addoption('-q', '--quiet', action="count",
- dest="quiet", default=0, help="decreate verbosity."),
- group._addoption('-r',
- action="store", dest="reportchars", default=None, metavar="chars",
- help="show extra test summary info as specified by chars (f)ailed, "
- "(E)error, (s)skipped, (x)failed, (X)passed.")
- group._addoption('-l', '--showlocals',
- action="store_true", dest="showlocals", default=False,
- help="show locals in tracebacks (disabled by default).")
- group._addoption('--report',
- action="store", dest="report", default=None, metavar="opts",
- help="(deprecated, use -r)")
- group._addoption('--tb', metavar="style",
- action="store", dest="tbstyle", default='long',
- type="choice", choices=['long', 'short', 'no', 'line', 'native'],
- help="traceback print mode (long/short/line/native/no).")
- group._addoption('--fulltrace',
- action="store_true", dest="fulltrace", default=False,
- help="don't cut any tracebacks (default is to cut).")
-
-def pytest_configure(config):
- config.option.verbose -= config.option.quiet
- # we try hard to make printing resilient against
- # later changes on FD level.
- stdout = py.std.sys.stdout
- if hasattr(os, 'dup') and hasattr(stdout, 'fileno'):
- try:
- newfd = os.dup(stdout.fileno())
- #print "got newfd", newfd
- except ValueError:
- pass
- else:
- stdout = os.fdopen(newfd, stdout.mode, 1)
- config._cleanup.append(lambda: stdout.close())
-
- reporter = TerminalReporter(config, stdout)
- config.pluginmanager.register(reporter, 'terminalreporter')
- if config.option.debug or config.option.traceconfig:
- def mywriter(tags, args):
- msg = " ".join(map(str, args))
- reporter.write_line("[traceconfig] " + msg)
- config.trace.root.setprocessor("pytest:config", mywriter)
-
-def getreportopt(config):
- reportopts = ""
- optvalue = config.option.report
- if optvalue:
- py.builtin.print_("DEPRECATED: use -r instead of --report option.",
- file=py.std.sys.stderr)
- if optvalue:
- for setting in optvalue.split(","):
- setting = setting.strip()
- if setting == "skipped":
- reportopts += "s"
- elif setting == "xfailed":
- reportopts += "x"
- reportchars = config.option.reportchars
- if reportchars:
- for char in reportchars:
- if char not in reportopts:
- reportopts += char
- return reportopts
-
-def pytest_report_teststatus(report):
- if report.passed:
- letter = "."
- elif report.skipped:
- letter = "s"
- elif report.failed:
- letter = "F"
- if report.when != "call":
- letter = "f"
- return report.outcome, letter, report.outcome.upper()
-
-class TerminalReporter:
- def __init__(self, config, file=None):
- self.config = config
- self.verbosity = self.config.option.verbose
- self.showheader = self.verbosity >= 0
- self.showfspath = self.verbosity >= 0
- self.showlongtestinfo = self.verbosity > 0
- self._numcollected = 0
-
- self.stats = {}
- self.curdir = py.path.local()
- if file is None:
- file = py.std.sys.stdout
- self._tw = py.io.TerminalWriter(file)
- self.currentfspath = None
- self.reportchars = getreportopt(config)
- self.hasmarkup = self._tw.hasmarkup
-
- def hasopt(self, char):
- char = {'xfailed': 'x', 'skipped': 's'}.get(char,char)
- return char in self.reportchars
-
- def write_fspath_result(self, fspath, res):
- if fspath != self.currentfspath:
- self.currentfspath = fspath
- #fspath = self.curdir.bestrelpath(fspath)
- self._tw.line()
- #relpath = self.curdir.bestrelpath(fspath)
- self._tw.write(fspath + " ")
- self._tw.write(res)
-
- def write_ensure_prefix(self, prefix, extra="", **kwargs):
- if self.currentfspath != prefix:
- self._tw.line()
- self.currentfspath = prefix
- self._tw.write(prefix)
- if extra:
- self._tw.write(extra, **kwargs)
- self.currentfspath = -2
-
- def ensure_newline(self):
- if self.currentfspath:
- self._tw.line()
- self.currentfspath = None
-
- def write(self, content, **markup):
- self._tw.write(content, **markup)
-
- def write_line(self, line, **markup):
- line = str(line)
- self.ensure_newline()
- self._tw.line(line, **markup)
-
- def rewrite(self, line, **markup):
- line = str(line)
- self._tw.write("\r" + line, **markup)
-
- def write_sep(self, sep, title=None, **markup):
- self.ensure_newline()
- self._tw.sep(sep, title, **markup)
-
- def pytest_internalerror(self, excrepr):
- for line in str(excrepr).split("\n"):
- self.write_line("INTERNALERROR> " + line)
- return 1
-
- def pytest_plugin_registered(self, plugin):
- if self.config.option.traceconfig:
- msg = "PLUGIN registered: %s" %(plugin,)
- # XXX this event may happen during setup/teardown time
- # which unfortunately captures our output here
- # which garbles our output if we use self.write_line
- self.write_line(msg)
-
- def pytest_deselected(self, items):
- self.stats.setdefault('deselected', []).extend(items)
-
- def pytest_runtest_logstart(self, nodeid, location):
- # ensure that the path is printed before the
- # 1st test of a module starts running
- fspath = nodeid.split("::")[0]
- if self.showlongtestinfo:
- line = self._locationline(fspath, *location)
- self.write_ensure_prefix(line, "")
- elif self.showfspath:
- self.write_fspath_result(fspath, "")
-
- def pytest_runtest_logreport(self, report):
- rep = report
- res = self.config.hook.pytest_report_teststatus(report=rep)
- cat, letter, word = res
- self.stats.setdefault(cat, []).append(rep)
- if not letter and not word:
- # probably passed setup/teardown
- return
- if self.verbosity <= 0:
- if not hasattr(rep, 'node') and self.showfspath:
- self.write_fspath_result(rep.fspath, letter)
- else:
- self._tw.write(letter)
- else:
- if isinstance(word, tuple):
- word, markup = word
- else:
- if rep.passed:
- markup = {'green':True}
- elif rep.failed:
- markup = {'red':True}
- elif rep.skipped:
- markup = {'yellow':True}
- line = self._locationline(str(rep.fspath), *rep.location)
- if not hasattr(rep, 'node'):
- self.write_ensure_prefix(line, word, **markup)
- #self._tw.write(word, **markup)
- else:
- self.ensure_newline()
- if hasattr(rep, 'node'):
- self._tw.write("[%s] " % rep.node.gateway.id)
- self._tw.write(word, **markup)
- self._tw.write(" " + line)
- self.currentfspath = -2
-
- def pytest_collection(self):
- if not self.hasmarkup:
- self.write("collecting ... ", bold=True)
-
- def pytest_collectreport(self, report):
- if report.failed:
- self.stats.setdefault("error", []).append(report)
- elif report.skipped:
- self.stats.setdefault("skipped", []).append(report)
- items = [x for x in report.result if isinstance(x, pytest.Item)]
- self._numcollected += len(items)
- if self.hasmarkup:
- #self.write_fspath_result(report.fspath, 'E')
- self.report_collect()
-
- def report_collect(self, final=False):
- errors = len(self.stats.get('error', []))
- skipped = len(self.stats.get('skipped', []))
- if final:
- line = "collected "
- else:
- line = "collecting "
- line += str(self._numcollected) + " items"
- if errors:
- line += " / %d errors" % errors
- if skipped:
- line += " / %d skipped" % skipped
- if self.hasmarkup:
- if final:
- line += " \n"
- self.rewrite(line, bold=True)
- else:
- self.write_line(line)
-
- def pytest_collection_modifyitems(self):
- self.report_collect(True)
-
- def pytest_sessionstart(self, session):
- self._sessionstarttime = py.std.time.time()
- if not self.showheader:
- return
- self.write_sep("=", "test session starts", bold=True)
- verinfo = ".".join(map(str, sys.version_info[:3]))
- msg = "platform %s -- Python %s" % (sys.platform, verinfo)
- if hasattr(sys, 'pypy_version_info'):
- verinfo = ".".join(map(str, sys.pypy_version_info[:3]))
- msg += "[pypy-%s-%s]" % (verinfo, sys.pypy_version_info[3])
- msg += " -- pytest-%s" % (py.test.__version__)
- if self.verbosity > 0 or self.config.option.debug or \
- getattr(self.config.option, 'pastebin', None):
- msg += " -- " + str(sys.executable)
- self.write_line(msg)
- lines = self.config.hook.pytest_report_header(config=self.config)
- lines.reverse()
- for line in flatten(lines):
- self.write_line(line)
-
- def pytest_collection_finish(self, session):
- if self.config.option.collectonly:
- self._printcollecteditems(session.items)
- if self.stats.get('failed'):
- self._tw.sep("!", "collection failures")
- for rep in self.stats.get('failed'):
- rep.toterminal(self._tw)
- return 1
- return 0
- if not self.showheader:
- return
- #for i, testarg in enumerate(self.config.args):
- # self.write_line("test path %d: %s" %(i+1, testarg))
-
- def _printcollecteditems(self, items):
- # to print out items and their parent collectors
- # we take care to leave out Instances aka ()
- # because later versions are going to get rid of them anyway
- if self.config.option.verbose < 0:
- if self.config.option.verbose < -1:
- counts = {}
- for item in items:
- name = item.nodeid.split('::', 1)[0]
- counts[name] = counts.get(name, 0) + 1
- for name, count in sorted(counts.items()):
- self._tw.line("%s: %d" % (name, count))
- else:
- for item in items:
- nodeid = item.nodeid
- nodeid = nodeid.replace("::()::", "::")
- self._tw.line(nodeid)
- return
- stack = []
- indent = ""
- for item in items:
- needed_collectors = item.listchain()[1:] # strip root node
- while stack:
- if stack == needed_collectors[:len(stack)]:
- break
- stack.pop()
- for col in needed_collectors[len(stack):]:
- stack.append(col)
- #if col.name == "()":
- # continue
- indent = (len(stack)-1) * " "
- self._tw.line("%s%s" %(indent, col))
-
- def pytest_sessionfinish(self, exitstatus, __multicall__):
- __multicall__.execute()
- self._tw.line("")
- if exitstatus in (0, 1, 2):
- self.summary_errors()
- self.summary_failures()
- self.config.hook.pytest_terminal_summary(terminalreporter=self)
- if exitstatus == 2:
- self._report_keyboardinterrupt()
- del self._keyboardinterrupt_memo
- self.summary_deselected()
- self.summary_stats()
-
- def pytest_keyboard_interrupt(self, excinfo):
- self._keyboardinterrupt_memo = excinfo.getrepr(funcargs=True)
-
- def pytest_unconfigure(self):
- if hasattr(self, '_keyboardinterrupt_memo'):
- self._report_keyboardinterrupt()
-
- def _report_keyboardinterrupt(self):
- excrepr = self._keyboardinterrupt_memo
- msg = excrepr.reprcrash.message
- self.write_sep("!", msg)
- if "KeyboardInterrupt" in msg:
- if self.config.option.fulltrace:
- excrepr.toterminal(self._tw)
- else:
- excrepr.reprcrash.toterminal(self._tw)
-
- def _locationline(self, collect_fspath, fspath, lineno, domain):
- # collect_fspath comes from testid which has a "/"-normalized path
- if fspath and fspath.replace("\\", "/") != collect_fspath:
- fspath = "%s <- %s" % (collect_fspath, fspath)
- if fspath:
- line = str(fspath)
- if lineno is not None:
- lineno += 1
- line += ":" + str(lineno)
- if domain:
- line += ": " + str(domain)
- else:
- line = "[location]"
- return line + " "
-
- def _getfailureheadline(self, rep):
- if hasattr(rep, 'location'):
- fspath, lineno, domain = rep.location
- return domain
- else:
- return "test session" # XXX?
-
- def _getcrashline(self, rep):
- try:
- return str(rep.longrepr.reprcrash)
- except AttributeError:
- try:
- return str(rep.longrepr)[:50]
- except AttributeError:
- return ""
-
- #
- # summaries for sessionfinish
- #
- def getreports(self, name):
- l = []
- for x in self.stats.get(name, []):
- if not hasattr(x, '_pdbshown'):
- l.append(x)
- return l
-
- def summary_failures(self):
- if self.config.option.tbstyle != "no":
- reports = self.getreports('failed')
- if not reports:
- return
- self.write_sep("=", "FAILURES")
- for rep in reports:
- if self.config.option.tbstyle == "line":
- line = self._getcrashline(rep)
- self.write_line(line)
- else:
- msg = self._getfailureheadline(rep)
- self.write_sep("_", msg)
- self._outrep_summary(rep)
-
- def summary_errors(self):
- if self.config.option.tbstyle != "no":
- reports = self.getreports('error')
- if not reports:
- return
- self.write_sep("=", "ERRORS")
- for rep in self.stats['error']:
- msg = self._getfailureheadline(rep)
- if not hasattr(rep, 'when'):
- # collect
- msg = "ERROR collecting " + msg
- elif rep.when == "setup":
- msg = "ERROR at setup of " + msg
- elif rep.when == "teardown":
- msg = "ERROR at teardown of " + msg
- self.write_sep("_", msg)
- self._outrep_summary(rep)
-
- def _outrep_summary(self, rep):
- rep.toterminal(self._tw)
- for secname, content in rep.sections:
- self._tw.sep("-", secname)
- if content[-1:] == "\n":
- content = content[:-1]
- self._tw.line(content)
-
- def summary_stats(self):
- session_duration = py.std.time.time() - self._sessionstarttime
-
- keys = "failed passed skipped deselected".split()
- for key in self.stats.keys():
- if key not in keys:
- keys.append(key)
- parts = []
- for key in keys:
- if key: # setup/teardown reports have an empty key, ignore them
- val = self.stats.get(key, None)
- if val:
- parts.append("%d %s" %(len(val), key))
- line = ", ".join(parts)
- # XXX coloring
- msg = "%s in %.2f seconds" %(line, session_duration)
- if self.verbosity >= 0:
- self.write_sep("=", msg, bold=True)
- else:
- self.write_line(msg, bold=True)
-
- def summary_deselected(self):
- if 'deselected' in self.stats:
- l = []
- k = self.config.option.keyword
- if k:
- l.append("-k%s" % k)
- m = self.config.option.markexpr
- if m:
- l.append("-m %r" % m)
- self.write_sep("=", "%d tests deselected by %r" %(
- len(self.stats['deselected']), " ".join(l)), bold=True)
-
-def repr_pythonversion(v=None):
- if v is None:
- v = sys.version_info
- try:
- return "%s.%s.%s-%s-%s" % v
- except (TypeError, ValueError):
- return str(v)
-
-def flatten(l):
- for x in l:
- if isinstance(x, (list, tuple)):
- for y in flatten(x):
- yield y
- else:
- yield x
-
diff --git a/_pytest/tmpdir.py b/_pytest/tmpdir.py
deleted file mode 100644
--- a/_pytest/tmpdir.py
+++ /dev/null
@@ -1,68 +0,0 @@
-""" support for providing temporary directories to test functions. """
-import pytest, py
-from _pytest.monkeypatch import monkeypatch
-
-class TempdirHandler:
- def __init__(self, config):
- self.config = config
- self.trace = config.trace.get("tmpdir")
-
- def ensuretemp(self, string, dir=1):
- """ (deprecated) return temporary directory path with
- the given string as the trailing part. It is usually
- better to use the 'tmpdir' function argument which
- provides an empty unique-per-test-invocation directory
- and is guaranteed to be empty.
- """
- #py.log._apiwarn(">1.1", "use tmpdir function argument")
- return self.getbasetemp().ensure(string, dir=dir)
-
- def mktemp(self, basename, numbered=True):
- basetemp = self.getbasetemp()
- if not numbered:
- p = basetemp.mkdir(basename)
- else:
- p = py.path.local.make_numbered_dir(prefix=basename,
- keep=0, rootdir=basetemp, lock_timeout=None)
- self.trace("mktemp", p)
- return p
-
- def getbasetemp(self):
- """ return base temporary directory. """
- try:
- return self._basetemp
- except AttributeError:
- basetemp = self.config.option.basetemp
- if basetemp:
- basetemp = py.path.local(basetemp)
- if basetemp.check():
- basetemp.remove()
- basetemp.mkdir()
- else:
- basetemp = py.path.local.make_numbered_dir(prefix='pytest-')
- self._basetemp = t = basetemp
- self.trace("new basetemp", t)
- return t
-
- def finish(self):
- self.trace("finish")
-
-def pytest_configure(config):
- mp = monkeypatch()
- t = TempdirHandler(config)
- config._cleanup.extend([mp.undo, t.finish])
- mp.setattr(config, '_tmpdirhandler', t, raising=False)
- mp.setattr(pytest, 'ensuretemp', t.ensuretemp, raising=False)
-
-def pytest_funcarg__tmpdir(request):
- """return a temporary directory path object
- which is unique to each test function invocation,
- created as a sub directory of the base temporary
- directory. The returned object is a `py.path.local`_
- path object.
- """
- name = request._pyfuncitem.name
- name = py.std.re.sub("[\W]", "_", name)
- x = request.config._tmpdirhandler.mktemp(name, numbered=True)
- return x
-
diff --git a/_pytest/unittest.py b/_pytest/unittest.py
deleted file mode 100644
--- a/_pytest/unittest.py
+++ /dev/null
@@ -1,165 +0,0 @@
-""" discovery and running of std-library "unittest" style tests. """
-import pytest, py
-import sys, pdb
-
-# for transfering markers
-from _pytest.python import transfer_markers
-
-def pytest_pycollect_makeitem(collector, name, obj):
- unittest = sys.modules.get('unittest')
- if unittest is None:
- return # nobody can have derived unittest.TestCase
- try:
- isunit = issubclass(obj, unittest.TestCase)
- except KeyboardInterrupt:
- raise
- except Exception:
- pass
- else:
- if isunit:
- return UnitTestCase(name, parent=collector)
-
-class UnitTestCase(pytest.Class):
- def collect(self):
- loader = py.std.unittest.TestLoader()
- module = self.getparent(pytest.Module).obj
- cls = self.obj
- for name in loader.getTestCaseNames(self.obj):
- x = getattr(self.obj, name)
- funcobj = getattr(x, 'im_func', x)
- transfer_markers(funcobj, cls, module)
- if hasattr(funcobj, 'todo'):
- pytest.mark.xfail(reason=str(funcobj.todo))(funcobj)
- yield TestCaseFunction(name, parent=self)
-
- def setup(self):
- meth = getattr(self.obj, 'setUpClass', None)
- if meth is not None:
- meth()
- super(UnitTestCase, self).setup()
-
- def teardown(self):
- meth = getattr(self.obj, 'tearDownClass', None)
- if meth is not None:
- meth()
- super(UnitTestCase, self).teardown()
-
-class TestCaseFunction(pytest.Function):
- _excinfo = None
-
- def setup(self):
- self._testcase = self.parent.obj(self.name)
- self._obj = getattr(self._testcase, self.name)
- if hasattr(self._testcase, 'skip'):
- pytest.skip(self._testcase.skip)
- if hasattr(self._obj, 'skip'):
- pytest.skip(self._obj.skip)
- if hasattr(self._testcase, 'setup_method'):
- self._testcase.setup_method(self._obj)
-
- def teardown(self):
- if hasattr(self._testcase, 'teardown_method'):
- self._testcase.teardown_method(self._obj)
-
- def startTest(self, testcase):
- pass
-
- def _addexcinfo(self, rawexcinfo):
- # unwrap potential exception info (see twisted trial support below)
- rawexcinfo = getattr(rawexcinfo, '_rawexcinfo', rawexcinfo)
- try:
- excinfo = py.code.ExceptionInfo(rawexcinfo)
- except TypeError:
- try:
- try:
- l = py.std.traceback.format_exception(*rawexcinfo)
- l.insert(0, "NOTE: Incompatible Exception Representation, "
- "displaying natively:\n\n")
- pytest.fail("".join(l), pytrace=False)
- except (pytest.fail.Exception, KeyboardInterrupt):
- raise
- except:
- pytest.fail("ERROR: Unknown Incompatible Exception "
- "representation:\n%r" %(rawexcinfo,), pytrace=False)
- except KeyboardInterrupt:
- raise
- except pytest.fail.Exception:
- excinfo = py.code.ExceptionInfo()
- self.__dict__.setdefault('_excinfo', []).append(excinfo)
-
- def addError(self, testcase, rawexcinfo):
- self._addexcinfo(rawexcinfo)
- def addFailure(self, testcase, rawexcinfo):
- self._addexcinfo(rawexcinfo)
-
- def addSkip(self, testcase, reason):
- try:
- pytest.skip(reason)
- except pytest.skip.Exception:
- self._addexcinfo(sys.exc_info())
-
- def addExpectedFailure(self, testcase, rawexcinfo, reason=""):
- try:
- pytest.xfail(str(reason))
- except pytest.xfail.Exception:
- self._addexcinfo(sys.exc_info())
-
- def addUnexpectedSuccess(self, testcase, reason=""):
- self._unexpectedsuccess = reason
-
- def addSuccess(self, testcase):
- pass
-
- def stopTest(self, testcase):
- pass
-
- def runtest(self):
- self._testcase(result=self)
-
- def _prunetraceback(self, excinfo):
- pytest.Function._prunetraceback(self, excinfo)
- traceback = excinfo.traceback.filter(
- lambda x:not x.frame.f_globals.get('__unittest'))
- if traceback:
- excinfo.traceback = traceback
-
- at pytest.mark.tryfirst
-def pytest_runtest_makereport(item, call):
- if isinstance(item, TestCaseFunction):
- if item._excinfo:
- call.excinfo = item._excinfo.pop(0)
- del call.result
-
-# twisted trial support
-def pytest_runtest_protocol(item, __multicall__):
- if isinstance(item, TestCaseFunction):
- if 'twisted.trial.unittest' in sys.modules:
- ut = sys.modules['twisted.python.failure']
- Failure__init__ = ut.Failure.__init__.im_func
- check_testcase_implements_trial_reporter()
- def excstore(self, exc_value=None, exc_type=None, exc_tb=None,
- captureVars=None):
- if exc_value is None:
- self._rawexcinfo = sys.exc_info()
- else:
- if exc_type is None:
- exc_type = type(exc_value)
- self._rawexcinfo = (exc_type, exc_value, exc_tb)
- try:
- Failure__init__(self, exc_value, exc_type, exc_tb,
- captureVars=captureVars)
- except TypeError:
- Failure__init__(self, exc_value, exc_type, exc_tb)
- ut.Failure.__init__ = excstore
- try:
- return __multicall__.execute()
- finally:
- ut.Failure.__init__ = Failure__init__
-
-def check_testcase_implements_trial_reporter(done=[]):
- if done:
- return
- from zope.interface import classImplements
- from twisted.trial.itrial import IReporter
- classImplements(TestCaseFunction, IReporter)
- done.append(1)
diff --git a/ctypes_configure/__init__.py b/ctypes_configure/__init__.py
deleted file mode 100644
diff --git a/ctypes_configure/cbuild.py b/ctypes_configure/cbuild.py
deleted file mode 100644
--- a/ctypes_configure/cbuild.py
+++ /dev/null
@@ -1,456 +0,0 @@
-
-import os, sys, inspect, re, imp, py
-from ctypes_configure import stdoutcapture
-import distutils
-
-debug = 0
-
-configdir = py.path.local.make_numbered_dir(prefix='ctypes_configure-')
-
-class ExternalCompilationInfo(object):
-
- _ATTRIBUTES = ['pre_include_lines', 'includes', 'include_dirs',
- 'post_include_lines', 'libraries', 'library_dirs',
- 'separate_module_sources', 'separate_module_files']
- _AVOID_DUPLICATES = ['separate_module_files', 'libraries', 'includes',
- 'include_dirs', 'library_dirs', 'separate_module_sources']
-
- def __init__(self,
- pre_include_lines = [],
- includes = [],
- include_dirs = [],
- post_include_lines = [],
- libraries = [],
- library_dirs = [],
- separate_module_sources = [],
- separate_module_files = []):
- """
- pre_include_lines: list of lines that should be put at the top
- of the generated .c files, before any #include. They shouldn't
- contain an #include themselves.
-
- includes: list of .h file names to be #include'd from the
- generated .c files.
-
- include_dirs: list of dir names that is passed to the C compiler
-
- post_include_lines: list of lines that should be put at the top
- of the generated .c files, after the #includes.
-
- libraries: list of library names that is passed to the linker
-
- library_dirs: list of dir names that is passed to the linker
-
- separate_module_sources: list of multiline strings that are
- each written to a .c file and compiled separately and linked
- later on. (If function prototypes are needed for other .c files
- to access this, they can be put in post_include_lines.)
-
- separate_module_files: list of .c file names that are compiled
- separately and linked later on. (If an .h file is needed for
- other .c files to access this, it can be put in includes.)
- """
- for name in self._ATTRIBUTES:
- value = locals()[name]
- assert isinstance(value, (list, tuple))
- setattr(self, name, tuple(value))
-
- def _value(self):
- return tuple([getattr(self, x) for x in self._ATTRIBUTES])
-
- def __hash__(self):
- return hash(self._value())
-
- def __eq__(self, other):
- return self.__class__ is other.__class__ and \
- self._value() == other._value()
-
- def __ne__(self, other):
- return not self == other
-
- def __repr__(self):
- info = []
- for attr in self._ATTRIBUTES:
- val = getattr(self, attr)
- info.append("%s=%s" % (attr, repr(val)))
- return "<ExternalCompilationInfo (%s)>" % ", ".join(info)
-
- def merge(self, *others):
- others = list(others)
- attrs = {}
- for name in self._ATTRIBUTES:
- if name not in self._AVOID_DUPLICATES:
- s = []
- for i in [self] + others:
- s += getattr(i, name)
- attrs[name] = s
- else:
- s = set()
- attr = []
- for one in [self] + others:
- for elem in getattr(one, name):
- if elem not in s:
- s.add(elem)
- attr.append(elem)
- attrs[name] = attr
- return ExternalCompilationInfo(**attrs)
-
- def write_c_header(self, fileobj):
- for line in self.pre_include_lines:
- print >> fileobj, line
- for path in self.includes:
- print >> fileobj, '#include <%s>' % (path,)
- for line in self.post_include_lines:
- print >> fileobj, line
-
- def _copy_attributes(self):
- d = {}
- for attr in self._ATTRIBUTES:
- d[attr] = getattr(self, attr)
- return d
-
- def convert_sources_to_files(self, cache_dir=None, being_main=False):
- if not self.separate_module_sources:
- return self
- if cache_dir is None:
- cache_dir = configdir.join('module_cache').ensure(dir=1)
- num = 0
- files = []
- for source in self.separate_module_sources:
- while 1:
- filename = cache_dir.join('module_%d.c' % num)
- num += 1
- if not filename.check():
- break
- f = filename.open("w")
- if being_main:
- f.write("#define PYPY_NOT_MAIN_FILE\n")
- self.write_c_header(f)
- source = str(source)
- f.write(source)
- if not source.endswith('\n'):
- f.write('\n')
- f.close()
- files.append(str(filename))
- d = self._copy_attributes()
- d['separate_module_sources'] = ()
- d['separate_module_files'] += tuple(files)
- return ExternalCompilationInfo(**d)
-
- def compile_shared_lib(self):
- self = self.convert_sources_to_files()
- if not self.separate_module_files:
- return self
- lib = compile_c_module([], 'externmod', self)
- d = self._copy_attributes()
- d['libraries'] += (lib,)
- d['separate_module_files'] = ()
- d['separate_module_sources'] = ()
- return ExternalCompilationInfo(**d)
-
-if sys.platform == 'win32':
- so_ext = '.dll'
-else:
- so_ext = '.so'
-
-def compiler_command():
- # e.g. for tcc, you might set this to
- # "tcc -shared -o %s.so %s.c"
- return os.getenv('PYPY_CC')
-
-def enable_fast_compilation():
- if sys.platform == 'win32':
- dash = '/'
- else:
- dash = '-'
- from distutils import sysconfig
- gcv = sysconfig.get_config_vars()
- opt = gcv.get('OPT') # not always existent
- if opt:
- opt = re.sub('%sO\d+' % dash, '%sO0' % dash, opt)
- else:
- opt = '%sO0' % dash
- gcv['OPT'] = opt
-
-def ensure_correct_math():
- if sys.platform != 'win32':
- return # so far
- from distutils import sysconfig
- gcv = sysconfig.get_config_vars()
- opt = gcv.get('OPT') # not always existent
- if opt and '/Op' not in opt:
- opt += '/Op'
- gcv['OPT'] = opt
-
-
-def try_compile(c_files, eci):
- try:
- build_executable(c_files, eci)
- result = True
- except (distutils.errors.CompileError,
- distutils.errors.LinkError):
- result = False
- return result
-
-def compile_c_module(cfiles, modbasename, eci, tmpdir=None):
- #try:
- # from distutils.log import set_threshold
- # set_threshold(10000)
- #except ImportError:
- # print "ERROR IMPORTING"
- # pass
- cfiles = [py.path.local(f) for f in cfiles]
- if tmpdir is None:
- tmpdir = configdir.join("module_cache").ensure(dir=1)
- num = 0
- cfiles += eci.separate_module_files
- include_dirs = list(eci.include_dirs)
- library_dirs = list(eci.library_dirs)
- if (sys.platform == 'darwin' or # support Fink & Darwinports
- sys.platform.startswith('freebsd')):
- for s in ('/sw/', '/opt/local/', '/usr/local/'):
- if s + 'include' not in include_dirs and \
- os.path.exists(s + 'include'):
- include_dirs.append(s + 'include')
- if s + 'lib' not in library_dirs and \
- os.path.exists(s + 'lib'):
- library_dirs.append(s + 'lib')
-
- num = 0
- modname = modbasename
- while 1:
- if not tmpdir.join(modname + so_ext).check():
- break
- num += 1
- modname = '%s_%d' % (modbasename, num)
-
- lastdir = tmpdir.chdir()
- libraries = eci.libraries
- ensure_correct_math()
- try:
- if debug: print "modname", modname
- c = stdoutcapture.Capture(mixed_out_err = True)
- try:
- try:
- if compiler_command():
- # GCC-ish options only
- from distutils import sysconfig
- gcv = sysconfig.get_config_vars()
- cmd = compiler_command().replace('%s',
- str(tmpdir.join(modname)))
- for dir in [gcv['INCLUDEPY']] + list(include_dirs):
- cmd += ' -I%s' % dir
- for dir in library_dirs:
- cmd += ' -L%s' % dir
- os.system(cmd)
- else:
- from distutils.dist import Distribution
- from distutils.extension import Extension
- from distutils.ccompiler import get_default_compiler
- saved_environ = os.environ.items()
- try:
- # distutils.core.setup() is really meant for end-user
- # interactive usage, because it eats most exceptions and
- # turn them into SystemExits. Instead, we directly
- # instantiate a Distribution, which also allows us to
- # ignore unwanted features like config files.
- extra_compile_args = []
- # ensure correct math on windows
- if sys.platform == 'win32':
- extra_compile_args.append('/Op') # get extra precision
- if get_default_compiler() == 'unix':
- old_version = False
- try:
- g = os.popen('gcc --version', 'r')
- verinfo = g.read()
- g.close()
- except (OSError, IOError):
- pass
- else:
- old_version = verinfo.startswith('2')
- if not old_version:
- extra_compile_args.extend(["-Wno-unused-label",
- "-Wno-unused-variable"])
- attrs = {
- 'name': "testmodule",
- 'ext_modules': [
- Extension(modname, [str(cfile) for cfile in cfiles],
- include_dirs=include_dirs,
- library_dirs=library_dirs,
- extra_compile_args=extra_compile_args,
- libraries=list(libraries),)
- ],
- 'script_name': 'setup.py',
- 'script_args': ['-q', 'build_ext', '--inplace', '--force'],
- }
- dist = Distribution(attrs)
- if not dist.parse_command_line():
- raise ValueError, "distutils cmdline parse error"
- dist.run_commands()
- finally:
- for key, value in saved_environ:
- if os.environ.get(key) != value:
- os.environ[key] = value
- finally:
- foutput, foutput = c.done()
- data = foutput.read()
- if data:
- fdump = open("%s.errors" % modname, "w")
- fdump.write(data)
- fdump.close()
- # XXX do we need to do some check on fout/ferr?
- # XXX not a nice way to import a module
- except:
- print >>sys.stderr, data
- raise
- finally:
- lastdir.chdir()
- return str(tmpdir.join(modname) + so_ext)
-
-def make_module_from_c(cfile, eci):
- cfile = py.path.local(cfile)
- modname = cfile.purebasename
- compile_c_module([cfile], modname, eci)
- return import_module_from_directory(cfile.dirpath(), modname)
-
-def import_module_from_directory(dir, modname):
- file, pathname, description = imp.find_module(modname, [str(dir)])
- try:
- mod = imp.load_module(modname, file, pathname, description)
- finally:
- if file:
- file.close()
- return mod
-
-
-def log_spawned_cmd(spawn):
- def spawn_and_log(cmd, *args, **kwds):
- if debug:
- print ' '.join(cmd)
- return spawn(cmd, *args, **kwds)
- return spawn_and_log
-
-
-class ProfOpt(object):
- #XXX assuming gcc style flags for now
- name = "profopt"
-
- def __init__(self, compiler):
- self.compiler = compiler
-
- def first(self):
- self.build('-fprofile-generate')
-
- def probe(self, exe, args):
- # 'args' is a single string typically containing spaces
- # and quotes, which represents several arguments.
- os.system("'%s' %s" % (exe, args))
-
- def after(self):
- self.build('-fprofile-use')
-
- def build(self, option):
- compiler = self.compiler
- compiler.compile_extra.append(option)
- compiler.link_extra.append(option)
- try:
- compiler._build()
- finally:
- compiler.compile_extra.pop()
- compiler.link_extra.pop()
-
-class CCompiler:
-
- def __init__(self, cfilenames, eci, outputfilename=None,
- compiler_exe=None, profbased=None):
- self.cfilenames = cfilenames
- ext = ''
- self.compile_extra = []
- self.link_extra = []
- self.libraries = list(eci.libraries)
- self.include_dirs = list(eci.include_dirs)
- self.library_dirs = list(eci.library_dirs)
- self.compiler_exe = compiler_exe
- self.profbased = profbased
- if not sys.platform in ('win32', 'darwin'): # xxx
- if 'm' not in self.libraries:
- self.libraries.append('m')
- if 'pthread' not in self.libraries:
- self.libraries.append('pthread')
- self.compile_extra += ['-O3', '-fomit-frame-pointer', '-pthread']
- self.link_extra += ['-pthread']
- if sys.platform == 'win32':
- self.link_extra += ['/DEBUG'] # generate .pdb file
- if (sys.platform == 'darwin' or # support Fink & Darwinports
- sys.platform.startswith('freebsd')):
- for s in ('/sw/', '/opt/local/', '/usr/local/'):
- if s + 'include' not in self.include_dirs and \
- os.path.exists(s + 'include'):
- self.include_dirs.append(s + 'include')
- if s + 'lib' not in self.library_dirs and \
- os.path.exists(s + 'lib'):
- self.library_dirs.append(s + 'lib')
- self.compile_extra += ['-O3', '-fomit-frame-pointer']
-
- if outputfilename is None:
- self.outputfilename = py.path.local(cfilenames[0]).new(ext=ext)
- else:
- self.outputfilename = py.path.local(outputfilename)
-
- def build(self, noerr=False):
- basename = self.outputfilename.new(ext='')
- data = ''
- try:
- saved_environ = os.environ.copy()
- c = stdoutcapture.Capture(mixed_out_err = True)
- try:
- self._build()
- finally:
- # workaround for a distutils bugs where some env vars can
- # become longer and longer every time it is used
- for key, value in saved_environ.items():
- if os.environ.get(key) != value:
- os.environ[key] = value
- foutput, foutput = c.done()
- data = foutput.read()
- if data:
- fdump = basename.new(ext='errors').open("w")
- fdump.write(data)
- fdump.close()
- except:
- if not noerr:
- print >>sys.stderr, data
- raise
-
- def _build(self):
- from distutils.ccompiler import new_compiler
- compiler = new_compiler(force=1)
- if self.compiler_exe is not None:
- for c in '''compiler compiler_so compiler_cxx
- linker_exe linker_so'''.split():
- compiler.executables[c][0] = self.compiler_exe
- compiler.spawn = log_spawned_cmd(compiler.spawn)
- objects = []
- for cfile in self.cfilenames:
- cfile = py.path.local(cfile)
- old = cfile.dirpath().chdir()
- try:
- res = compiler.compile([cfile.basename],
- include_dirs=self.include_dirs,
- extra_preargs=self.compile_extra)
- assert len(res) == 1
- cobjfile = py.path.local(res[0])
- assert cobjfile.check()
- objects.append(str(cobjfile))
- finally:
- old.chdir()
- compiler.link_executable(objects, str(self.outputfilename),
- libraries=self.libraries,
- extra_preargs=self.link_extra,
- library_dirs=self.library_dirs)
-
-def build_executable(*args, **kwds):
- noerr = kwds.pop('noerr', False)
- compiler = CCompiler(*args, **kwds)
- compiler.build(noerr=noerr)
- return str(compiler.outputfilename)
diff --git a/ctypes_configure/configure.py b/ctypes_configure/configure.py
deleted file mode 100755
--- a/ctypes_configure/configure.py
+++ /dev/null
@@ -1,621 +0,0 @@
-#! /usr/bin/env python
-
-import os, py, sys
-import ctypes
-from ctypes_configure.cbuild import build_executable, configdir, try_compile
-from ctypes_configure.cbuild import ExternalCompilationInfo
-import distutils
-
-# ____________________________________________________________
-#
-# Helpers for simple cases
-
-def eci_from_header(c_header_source):
- return ExternalCompilationInfo(
- pre_include_lines=c_header_source.split("\n")
- )
-
-
-def getstruct(name, c_header_source, interesting_fields):
- class CConfig:
- _compilation_info_ = eci_from_header(c_header_source)
- STRUCT = Struct(name, interesting_fields)
- return configure(CConfig)['STRUCT']
-
-def getsimpletype(name, c_header_source, ctype_hint=ctypes.c_int):
- class CConfig:
- _compilation_info_ = eci_from_header(c_header_source)
- TYPE = SimpleType(name, ctype_hint)
- return configure(CConfig)['TYPE']
-
-def getconstantinteger(name, c_header_source):
- class CConfig:
- _compilation_info_ = eci_from_header(c_header_source)
- CONST = ConstantInteger(name)
- return configure(CConfig)['CONST']
-
-def getdefined(macro, c_header_source):
- class CConfig:
- _compilation_info_ = eci_from_header(c_header_source)
- DEFINED = Defined(macro)
- return configure(CConfig)['DEFINED']
-
-def has(name, c_header_source):
- class CConfig:
- _compilation_info_ = eci_from_header(c_header_source)
- HAS = Has(name)
- return configure(CConfig)['HAS']
-
-def check_eci(eci):
- """Check if a given ExternalCompilationInfo compiles and links."""
- class CConfig:
- _compilation_info_ = eci
- WORKS = Works()
- return configure(CConfig)['WORKS']
-
-def sizeof(name, eci, **kwds):
- class CConfig:
- _compilation_info_ = eci
- SIZE = SizeOf(name)
- for k, v in kwds.items():
- setattr(CConfig, k, v)
- return configure(CConfig)['SIZE']
-
-def memory_alignment():
- """Return the alignment (in bytes) of memory allocations.
- This is enough to make sure a structure with pointers and 'double'
- fields is properly aligned."""
- global _memory_alignment
- if _memory_alignment is None:
- S = getstruct('struct memory_alignment_test', """
- struct memory_alignment_test {
- double d;
- void* p;
- };
- """, [])
- result = ctypes.alignment(S)
- assert result & (result-1) == 0, "not a power of two??"
- _memory_alignment = result
- return _memory_alignment
-_memory_alignment = None
-
-# ____________________________________________________________
-#
-# General interface
-
-class ConfigResult:
- def __init__(self, CConfig, info, entries):
- self.CConfig = CConfig
- self.result = {}
- self.info = info
- self.entries = entries
-
- def get_entry_result(self, entry):
- try:
- return self.result[entry]
- except KeyError:
- pass
- name = self.entries[entry]
- info = self.info[name]
- self.result[entry] = entry.build_result(info, self)
-
- def get_result(self):
- return dict([(name, self.result[entry])
- for entry, name in self.entries.iteritems()])
-
-
-class _CWriter(object):
- """ A simple class which aggregates config parts
- """
- def __init__(self, CConfig):
- self.path = uniquefilepath()
- self.f = self.path.open("w")
- self.config = CConfig
-
- def write_header(self):
- f = self.f
- CConfig = self.config
- CConfig._compilation_info_.write_c_header(f)
- print >> f, C_HEADER
- print >> f
-
- def write_entry(self, key, entry):
- f = self.f
- print >> f, 'void dump_section_%s(void) {' % (key,)
- for line in entry.prepare_code():
- if line and line[0] != '#':
- line = '\t' + line
- print >> f, line
- print >> f, '}'
- print >> f
-
- def write_entry_main(self, key):
- print >> self.f, '\tprintf("-+- %s\\n");' % (key,)
- print >> self.f, '\tdump_section_%s();' % (key,)
- print >> self.f, '\tprintf("---\\n");'
-
- def start_main(self):
- print >> self.f, 'int main(int argc, char *argv[]) {'
-
- def close(self):
- f = self.f
- print >> f, '\treturn 0;'
- print >> f, '}'
- f.close()
-
- def ask_gcc(self, question):
- self.start_main()
- self.f.write(question + "\n")
- self.close()
- eci = self.config._compilation_info_
- return try_compile([self.path], eci)
-
-
-def configure(CConfig, noerr=False):
- """Examine the local system by running the C compiler.
- The CConfig class contains CConfigEntry attribues that describe
- what should be inspected; configure() returns a dict mapping
- names to the results.
- """
- for attr in ['_includes_', '_libraries_', '_sources_', '_library_dirs_',
- '_include_dirs_', '_header_']:
- assert not hasattr(CConfig, attr), "Found legacy attribut %s on CConfig" % (attr,)
- entries = []
- for key in dir(CConfig):
- value = getattr(CConfig, key)
- if isinstance(value, CConfigEntry):
- entries.append((key, value))
-
- if entries: # can be empty if there are only CConfigSingleEntries
- writer = _CWriter(CConfig)
- writer.write_header()
- for key, entry in entries:
- writer.write_entry(key, entry)
-
- f = writer.f
- writer.start_main()
- for key, entry in entries:
- writer.write_entry_main(key)
- writer.close()
-
- eci = CConfig._compilation_info_
- infolist = list(run_example_code(writer.path, eci, noerr=noerr))
- assert len(infolist) == len(entries)
-
- resultinfo = {}
- resultentries = {}
- for info, (key, entry) in zip(infolist, entries):
- resultinfo[key] = info
- resultentries[entry] = key
-
- result = ConfigResult(CConfig, resultinfo, resultentries)
- for name, entry in entries:
- result.get_entry_result(entry)
- res = result.get_result()
- else:
- res = {}
-
- for key in dir(CConfig):
- value = getattr(CConfig, key)
- if isinstance(value, CConfigSingleEntry):
- writer = _CWriter(CConfig)
- writer.write_header()
- res[key] = value.question(writer.ask_gcc)
- return res
-
-# ____________________________________________________________
-
-
-class CConfigEntry(object):
- "Abstract base class."
-
-class Struct(CConfigEntry):
- """An entry in a CConfig class that stands for an externally
- defined structure.
- """
- def __init__(self, name, interesting_fields, ifdef=None):
- self.name = name
- self.interesting_fields = interesting_fields
- self.ifdef = ifdef
-
- def prepare_code(self):
- if self.ifdef is not None:
- yield '#ifdef %s' % (self.ifdef,)
- yield 'typedef %s ctypesplatcheck_t;' % (self.name,)
- yield 'typedef struct {'
- yield ' char c;'
- yield ' ctypesplatcheck_t s;'
- yield '} ctypesplatcheck2_t;'
- yield ''
- yield 'ctypesplatcheck_t s;'
- if self.ifdef is not None:
- yield 'dump("defined", 1);'
- yield 'dump("align", offsetof(ctypesplatcheck2_t, s));'
- yield 'dump("size", sizeof(ctypesplatcheck_t));'
- for fieldname, fieldtype in self.interesting_fields:
- yield 'dump("fldofs %s", offsetof(ctypesplatcheck_t, %s));'%(
- fieldname, fieldname)
- yield 'dump("fldsize %s", sizeof(s.%s));' % (
- fieldname, fieldname)
- if fieldtype in integer_class:
- yield 's.%s = 0; s.%s = ~s.%s;' % (fieldname,
- fieldname,
- fieldname)
- yield 'dump("fldunsigned %s", s.%s > 0);' % (fieldname,
- fieldname)
- if self.ifdef is not None:
- yield '#else'
- yield 'dump("defined", 0);'
- yield '#endif'
-
- def build_result(self, info, config_result):
- if self.ifdef is not None:
- if not info['defined']:
- return None
- alignment = 1
- layout = [None] * info['size']
- for fieldname, fieldtype in self.interesting_fields:
- if isinstance(fieldtype, Struct):
- offset = info['fldofs ' + fieldname]
- size = info['fldsize ' + fieldname]
- c_fieldtype = config_result.get_entry_result(fieldtype)
- layout_addfield(layout, offset, c_fieldtype, fieldname)
- alignment = max(alignment, ctype_alignment(c_fieldtype))
- else:
- offset = info['fldofs ' + fieldname]
- size = info['fldsize ' + fieldname]
- sign = info.get('fldunsigned ' + fieldname, False)
- if (size, sign) != size_and_sign(fieldtype):
- fieldtype = fixup_ctype(fieldtype, fieldname, (size, sign))
- layout_addfield(layout, offset, fieldtype, fieldname)
- alignment = max(alignment, ctype_alignment(fieldtype))
-
- # try to enforce the same alignment as the one of the original
- # structure
- if alignment < info['align']:
- choices = [ctype for ctype in alignment_types
- if ctype_alignment(ctype) == info['align']]
- assert choices, "unsupported alignment %d" % (info['align'],)
- choices = [(ctypes.sizeof(ctype), i, ctype)
- for i, ctype in enumerate(choices)]
- csize, _, ctype = min(choices)
- for i in range(0, info['size'] - csize + 1, info['align']):
- if layout[i:i+csize] == [None] * csize:
- layout_addfield(layout, i, ctype, '_alignment')
- break
- else:
- raise AssertionError("unenforceable alignment %d" % (
- info['align'],))
-
- n = 0
- for i, cell in enumerate(layout):
- if cell is not None:
- continue
- layout_addfield(layout, i, ctypes.c_char, '_pad%d' % (n,))
- n += 1
-
- # build the ctypes Structure
- seen = {}
- fields = []
- for cell in layout:
- if cell in seen:
- continue
- fields.append((cell.name, cell.ctype))
- seen[cell] = True
-
- class S(ctypes.Structure):
- _fields_ = fields
- name = self.name
- if name.startswith('struct '):
- name = name[7:]
- S.__name__ = name
- return S
-
-class SimpleType(CConfigEntry):
- """An entry in a CConfig class that stands for an externally
- defined simple numeric type.
- """
- def __init__(self, name, ctype_hint=ctypes.c_int, ifdef=None):
- self.name = name
- self.ctype_hint = ctype_hint
- self.ifdef = ifdef
-
- def prepare_code(self):
- if self.ifdef is not None:
- yield '#ifdef %s' % (self.ifdef,)
- yield 'typedef %s ctypesplatcheck_t;' % (self.name,)
- yield ''
- yield 'ctypesplatcheck_t x;'
- if self.ifdef is not None:
- yield 'dump("defined", 1);'
- yield 'dump("size", sizeof(ctypesplatcheck_t));'
- if self.ctype_hint in integer_class:
- yield 'x = 0; x = ~x;'
- yield 'dump("unsigned", x > 0);'
- if self.ifdef is not None:
- yield '#else'
- yield 'dump("defined", 0);'
- yield '#endif'
-
- def build_result(self, info, config_result):
- if self.ifdef is not None and not info['defined']:
- return None
- size = info['size']
- sign = info.get('unsigned', False)
- ctype = self.ctype_hint
- if (size, sign) != size_and_sign(ctype):
- ctype = fixup_ctype(ctype, self.name, (size, sign))
- return ctype
-
-class ConstantInteger(CConfigEntry):
- """An entry in a CConfig class that stands for an externally
- defined integer constant.
- """
- def __init__(self, name):
- self.name = name
-
- def prepare_code(self):
- yield 'if ((%s) < 0) {' % (self.name,)
- yield ' long long x = (long long)(%s);' % (self.name,)
- yield ' printf("value: %lld\\n", x);'
- yield '} else {'
- yield ' unsigned long long x = (unsigned long long)(%s);' % (
- self.name,)
- yield ' printf("value: %llu\\n", x);'
- yield '}'
-
- def build_result(self, info, config_result):
- return info['value']
-
-class DefinedConstantInteger(CConfigEntry):
- """An entry in a CConfig class that stands for an externally
- defined integer constant. If not #defined the value will be None.
- """
- def __init__(self, macro):
- self.name = self.macro = macro
-
- def prepare_code(self):
- yield '#ifdef %s' % self.macro
- yield 'dump("defined", 1);'
- yield 'if ((%s) < 0) {' % (self.macro,)
- yield ' long long x = (long long)(%s);' % (self.macro,)
- yield ' printf("value: %lld\\n", x);'
- yield '} else {'
- yield ' unsigned long long x = (unsigned long long)(%s);' % (
- self.macro,)
- yield ' printf("value: %llu\\n", x);'
- yield '}'
- yield '#else'
- yield 'dump("defined", 0);'
- yield '#endif'
-
- def build_result(self, info, config_result):
- if info["defined"]:
- return info['value']
- return None
-
-
-class DefinedConstantString(CConfigEntry):
- """
- """
- def __init__(self, macro):
- self.macro = macro
- self.name = macro
-
- def prepare_code(self):
- yield '#ifdef %s' % self.macro
- yield 'int i;'
- yield 'char *p = %s;' % self.macro
- yield 'dump("defined", 1);'
- yield 'for (i = 0; p[i] != 0; i++ ) {'
- yield ' printf("value_%d: %d\\n", i, (int)(unsigned char)p[i]);'
- yield '}'
- yield '#else'
- yield 'dump("defined", 0);'
- yield '#endif'
-
- def build_result(self, info, config_result):
- if info["defined"]:
- string = ''
- d = 0
- while info.has_key('value_%d' % d):
- string += chr(info['value_%d' % d])
- d += 1
- return string
- return None
-
-
-class Defined(CConfigEntry):
- """A boolean, corresponding to an #ifdef.
- """
- def __init__(self, macro):
- self.macro = macro
- self.name = macro
-
- def prepare_code(self):
- yield '#ifdef %s' % (self.macro,)
- yield 'dump("defined", 1);'
- yield '#else'
- yield 'dump("defined", 0);'
- yield '#endif'
-
- def build_result(self, info, config_result):
- return bool(info['defined'])
-
-class CConfigSingleEntry(object):
- """ An abstract class of type which requires
- gcc succeeding/failing instead of only asking
- """
- pass
-
-class Has(CConfigSingleEntry):
- def __init__(self, name):
- self.name = name
-
- def question(self, ask_gcc):
- return ask_gcc(self.name + ';')
-
-class Works(CConfigSingleEntry):
- def question(self, ask_gcc):
- return ask_gcc("")
-
-class SizeOf(CConfigEntry):
- """An entry in a CConfig class that stands for
- some external opaque type
- """
- def __init__(self, name):
- self.name = name
-
- def prepare_code(self):
- yield 'dump("size", sizeof(%s));' % self.name
-
- def build_result(self, info, config_result):
- return info['size']
-
-# ____________________________________________________________
-#
-# internal helpers
-
-def ctype_alignment(c_type):
- if issubclass(c_type, ctypes.Structure):
- return max([ctype_alignment(fld_type)
- for fld_name, fld_type in c_type._fields_])
-
- return ctypes.alignment(c_type)
-
-def uniquefilepath(LAST=[0]):
- i = LAST[0]
- LAST[0] += 1
- return configdir.join('ctypesplatcheck_%d.c' % i)
-
-alignment_types = [
- ctypes.c_short,
- ctypes.c_int,
- ctypes.c_long,
- ctypes.c_float,
- ctypes.c_double,
- ctypes.c_char_p,
- ctypes.c_void_p,
- ctypes.c_longlong,
- ctypes.c_wchar,
- ctypes.c_wchar_p,
- ]
-
-integer_class = [ctypes.c_byte, ctypes.c_ubyte,
- ctypes.c_short, ctypes.c_ushort,
- ctypes.c_int, ctypes.c_uint,
- ctypes.c_long, ctypes.c_ulong,
- ctypes.c_longlong, ctypes.c_ulonglong,
- ]
-float_class = [ctypes.c_float, ctypes.c_double]
-
-class Field(object):
- def __init__(self, name, ctype):
- self.name = name
- self.ctype = ctype
- def __repr__(self):
- return '<field %s: %s>' % (self.name, self.ctype)
-
-def layout_addfield(layout, offset, ctype, prefix):
- size = ctypes.sizeof(ctype)
- name = prefix
- i = 0
- while name in layout:
- i += 1
- name = '%s_%d' % (prefix, i)
- field = Field(name, ctype)
- for i in range(offset, offset+size):
- assert layout[i] is None, "%s overlaps %r" % (fieldname, layout[i])
- layout[i] = field
- return field
-
-def size_and_sign(ctype):
- return (ctypes.sizeof(ctype),
- ctype in integer_class and ctype(-1).value > 0)
-
-def fixup_ctype(fieldtype, fieldname, expected_size_and_sign):
- for typeclass in [integer_class, float_class]:
- if fieldtype in typeclass:
- for ctype in typeclass:
- if size_and_sign(ctype) == expected_size_and_sign:
- return ctype
- if (hasattr(fieldtype, '_length_')
- and getattr(fieldtype, '_type_', None) == ctypes.c_char):
- # for now, assume it is an array of chars; otherwise we'd also
- # have to check the exact integer type of the elements of the array
- size, sign = expected_size_and_sign
- return ctypes.c_char * size
- if (hasattr(fieldtype, '_length_')
- and getattr(fieldtype, '_type_', None) == ctypes.c_ubyte):
- # grumble, fields of type 'c_char array' have automatic cast-to-
- # Python-string behavior in ctypes, which may not be what you
- # want, so here is the same with c_ubytes instead...
- size, sign = expected_size_and_sign
- return ctypes.c_ubyte * size
- raise TypeError("conflicting field type %r for %r" % (fieldtype,
- fieldname))
-
-
-C_HEADER = """
-#include <stdio.h>
-#include <stddef.h> /* for offsetof() */
-#ifndef _WIN32
-# include <stdint.h> /* FreeBSD: for uint64_t */
-#endif
-
-void dump(char* key, int value) {
- printf("%s: %d\\n", key, value);
-}
-"""
-
-def run_example_code(filepath, eci, noerr=False):
- executable = build_executable([filepath], eci, noerr=noerr)
- output = py.process.cmdexec(executable)
- section = None
- for line in output.splitlines():
- line = line.strip()
- if line.startswith('-+- '): # start of a new section
- section = {}
- elif line == '---': # section end
- assert section is not None
- yield section
- section = None
- elif line:
- assert section is not None
- key, value = line.split(': ')
- section[key] = int(value)
-
-# ____________________________________________________________
-
-def get_python_include_dir():
- from distutils import sysconfig
- gcv = sysconfig.get_config_vars()
- return gcv['INCLUDEPY']
-
-if __name__ == '__main__':
- doc = """Example:
-
- ctypes_platform.py -h sys/types.h -h netinet/in.h
- 'struct sockaddr_in'
- sin_port c_int
- """
- import sys, getopt
- opts, args = getopt.gnu_getopt(sys.argv[1:], 'h:')
- if not args:
- print >> sys.stderr, doc
- else:
- assert len(args) % 2 == 1
- headers = []
- for opt, value in opts:
- if opt == '-h':
- headers.append('#include <%s>' % (value,))
- name = args[0]
- fields = []
- for i in range(1, len(args), 2):
- ctype = getattr(ctypes, args[i+1])
- fields.append((args[i], ctype))
-
- S = getstruct(name, '\n'.join(headers), fields)
-
- for key, value in S._fields_:
- print key, value
diff --git a/ctypes_configure/doc/configure.html b/ctypes_configure/doc/configure.html
deleted file mode 100644
--- a/ctypes_configure/doc/configure.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=latin1" />
-<meta name="generator" content="Docutils 0.4.1: http://docutils.sourceforge.net/" />
-<title>ctypes configure</title>
-</head>
-<body>
-<div class="document" id="ctypes-configure">
-<h1 class="title">ctypes configure</h1>
-<div class="section">
-<h1><a id="idea" name="idea">idea</a></h1>
-<p>One of ctypes problems is that ctypes programs are usually not very
-platform-independent. We created ctypes_configure, which invokes gcc
More information about the pypy-commit
mailing list