diff --git a/_pytest/compat.py b/_pytest/compat.py index 5be7f7a2a..c06e3f4ca 100644 --- a/_pytest/compat.py +++ b/_pytest/compat.py @@ -291,3 +291,12 @@ else: def getvalue(self): return self.buffer.getvalue().decode('UTF-8') + +class FuncargnamesCompatAttr(object): + """ helper class so that Metafunc, Function and FixtureRequest + don't need to each define the "funcargnames" compatibility attribute. + """ + @property + def funcargnames(self): + """ alias attribute for ``fixturenames`` for pre-2.3 compatibility""" + return self.fixturenames diff --git a/_pytest/debugging.py b/_pytest/debugging.py index 6c6bd6a02..ad81fedc0 100644 --- a/_pytest/debugging.py +++ b/_pytest/debugging.py @@ -3,7 +3,6 @@ from __future__ import absolute_import, division, print_function import pdb import sys -import pytest def pytest_addoption(parser): @@ -35,7 +34,7 @@ def pytest_configure(config): pytestPDB._config = None pytestPDB._pdb_cls = pdb.Pdb - pdb.set_trace = pytest.set_trace + pdb.set_trace = pytestPDB.set_trace pytestPDB._pluginmanager = config.pluginmanager pytestPDB._config = config pytestPDB._pdb_cls = pdb_cls @@ -75,7 +74,7 @@ class PdbInvoke(object): def pytest_internalerror(self, excrepr, excinfo): for line in str(excrepr).split("\n"): - sys.stderr.write("INTERNALERROR> %s\n" %line) + sys.stderr.write("INTERNALERROR> %s\n" % line) sys.stderr.flush() tb = _postmortem_traceback(excinfo) post_mortem(tb) diff --git a/_pytest/fixtures.py b/_pytest/fixtures.py index 16481ca1c..2710da792 100644 --- a/_pytest/fixtures.py +++ b/_pytest/fixtures.py @@ -4,7 +4,6 @@ import sys from py._code.code import FormattedExcinfo import py -import pytest import warnings import inspect @@ -17,12 +16,15 @@ from _pytest.compat import ( getlocation, getfuncargnames, safe_getattr, ) +from _pytest.runner import fail +from _pytest.compat import FuncargnamesCompatAttr +from _pytest import python def pytest_sessionstart(session): scopename2class.update({ - 'class': pytest.Class, - 'module': pytest.Module, - 'function': pytest.Item, + 'class': python.Class, + 'module': python.Module, + 'function': _pytest.main.Item, }) session._fixturemanager = FixtureManager(session) @@ -97,7 +99,7 @@ def add_funcarg_pseudo_fixture_def(collector, metafunc, fixturemanager): if scope != "function": node = get_scope_node(collector, scope) if node is None: - assert scope == "class" and isinstance(collector, pytest.Module) + assert scope == "class" and isinstance(collector, _pytest.python.Module) # use module-level collector for class-scope (for now) node = collector if node and argname in node._name2pseudofixturedef: @@ -213,17 +215,6 @@ def slice_items(items, ignore, scoped_argkeys_cache): return items, None, None, None - -class FuncargnamesCompatAttr(object): - """ helper class so that Metafunc, Function and FixtureRequest - don't need to each define the "funcargnames" compatibility attribute. - """ - @property - def funcargnames(self): - """ alias attribute for ``fixturenames`` for pre-2.3 compatibility""" - return self.fixturenames - - def fillfixtures(function): """ fill missing funcargs for a test function. """ try: @@ -319,7 +310,7 @@ class FixtureRequest(FuncargnamesCompatAttr): @scopeproperty("class") def cls(self): """ class (can be None) where the test function was collected. """ - clscol = self._pyfuncitem.getparent(pytest.Class) + clscol = self._pyfuncitem.getparent(_pytest.python.Class) if clscol: return clscol.obj @@ -337,7 +328,7 @@ class FixtureRequest(FuncargnamesCompatAttr): @scopeproperty() def module(self): """ python module object where the test function was collected. """ - return self._pyfuncitem.getparent(pytest.Module).obj + return self._pyfuncitem.getparent(_pytest.python.Module).obj @scopeproperty() def fspath(self): @@ -500,7 +491,7 @@ class FixtureRequest(FuncargnamesCompatAttr): source_lineno, ) ) - pytest.fail(msg) + fail(msg) else: # indices might not be set if old-style metafunc.addcall() was used param_index = funcitem.callspec.indices.get(argname, 0) @@ -533,10 +524,10 @@ class FixtureRequest(FuncargnamesCompatAttr): if scopemismatch(invoking_scope, requested_scope): # try to report something helpful lines = self._factorytraceback() - pytest.fail("ScopeMismatch: You tried to access the %r scoped " - "fixture %r with a %r scoped request object, " - "involved factories\n%s" %( - (requested_scope, argname, invoking_scope, "\n".join(lines))), + fail("ScopeMismatch: You tried to access the %r scoped " + "fixture %r with a %r scoped request object, " + "involved factories\n%s" % ( + (requested_scope, argname, invoking_scope, "\n".join(lines))), pytrace=False) def _factorytraceback(self): @@ -546,7 +537,7 @@ class FixtureRequest(FuncargnamesCompatAttr): fs, lineno = getfslineno(factory) p = self._pyfuncitem.session.fspath.bestrelpath(fs) args = _format_args(factory) - lines.append("%s:%d: def %s%s" %( + lines.append("%s:%d: def %s%s" % ( p, lineno, factory.__name__, args)) return lines diff --git a/_pytest/main.py b/_pytest/main.py index da4ae8e41..623273655 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -8,14 +8,13 @@ import sys import _pytest import _pytest._code import py -import pytest try: from collections import MutableMapping as MappingMixin except ImportError: from UserDict import DictMixin as MappingMixin -from _pytest.config import directory_arg -from _pytest.runner import collect_one_node +from _pytest.config import directory_arg, UsageError, hookimpl +from _pytest.runner import collect_one_node, exit tracebackcutdir = py.path.local(_pytest.__file__).dirpath() @@ -27,6 +26,7 @@ EXIT_INTERNALERROR = 3 EXIT_USAGEERROR = 4 EXIT_NOTESTSCOLLECTED = 5 + def pytest_addoption(parser): parser.addini("norecursedirs", "directory patterns to avoid for recursion", type="args", default=['.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg', 'venv']) @@ -78,7 +78,7 @@ def pytest_addoption(parser): def pytest_configure(config): - pytest.config = config # compatibility + pytest.config = config # compatibiltiy def wrap_session(config, doit): @@ -93,12 +93,11 @@ def wrap_session(config, doit): config.hook.pytest_sessionstart(session=session) initstate = 2 session.exitstatus = doit(config, session) or 0 - except pytest.UsageError: + except UsageError: raise except KeyboardInterrupt: excinfo = _pytest._code.ExceptionInfo() - if initstate < 2 and isinstance( - excinfo.value, pytest.exit.Exception): + if initstate < 2 and isinstance(excinfo.value, exit.Exception): sys.stderr.write('{0}: {1}\n'.format( excinfo.typename, excinfo.value.msg)) config.hook.pytest_keyboard_interrupt(excinfo=excinfo) @@ -120,9 +119,11 @@ def wrap_session(config, doit): config._ensure_unconfigure() 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. """ @@ -134,9 +135,11 @@ def _main(config, session): elif session.testscollected == 0: return EXIT_NOTESTSCOLLECTED + def pytest_collection(session): return session.perform_collect() + def pytest_runtestloop(session): if (session.testsfailed and not session.config.option.continue_on_collection_errors): @@ -153,6 +156,7 @@ def pytest_runtestloop(session): 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) @@ -200,7 +204,7 @@ class _CompatProperty(object): # "usage of {owner!r}.{name} is deprecated, please use pytest.{name} instead".format( # name=self.name, owner=type(owner).__name__), # PendingDeprecationWarning, stacklevel=2) - return getattr(pytest, self.name) + return getattr(__import__('pytest'), self.name) @@ -284,7 +288,7 @@ class Node(object): def _getcustomclass(self, name): maybe_compatprop = getattr(type(self), name) if isinstance(maybe_compatprop, _CompatProperty): - return getattr(pytest, name) + return getattr(__import__('pytest'), name) else: cls = getattr(self, name) # TODO: reenable in the features branch @@ -367,9 +371,9 @@ class Node(object): ``marker`` can be a string or pytest.mark.* instance. """ - from _pytest.mark import MarkDecorator + from _pytest.mark import MarkDecorator, MARK_GEN if isinstance(marker, py.builtin._basestring): - marker = getattr(pytest.mark, marker) + marker = getattr(MARK_GEN, marker) elif not isinstance(marker, MarkDecorator): raise ValueError("is not a string or pytest.mark.* Marker") self.keywords[marker.name] = marker @@ -555,12 +559,12 @@ class Session(FSCollector): def _makeid(self): return "" - @pytest.hookimpl(tryfirst=True) + @hookimpl(tryfirst=True) def pytest_collectstart(self): if self.shouldstop: raise self.Interrupted(self.shouldstop) - @pytest.hookimpl(tryfirst=True) + @hookimpl(tryfirst=True) def pytest_runtest_logreport(self, report): if report.failed and not hasattr(report, 'wasxfail'): self.testsfailed += 1 @@ -619,8 +623,8 @@ class Session(FSCollector): for arg, exc in self._notfound: line = "(no name %r in any of %r)" % (arg, exc.args[0]) errors.append("not found: %s\n%s" % (arg, line)) - #XXX: test this - raise pytest.UsageError(*errors) + # XXX: test this + raise UsageError(*errors) if not genitems: return rep.result else: @@ -648,7 +652,7 @@ class Session(FSCollector): names = self._parsearg(arg) path = names.pop(0) if path.check(dir=1): - assert not names, "invalid arg %r" %(arg,) + 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): @@ -707,9 +711,11 @@ class Session(FSCollector): path = self.config.invocation_dir.join(relpath, abs=True) if not path.check(): if self.config.option.pyargs: - raise pytest.UsageError("file or package not found: " + arg + " (missing __init__.py?)") + raise UsageError( + "file or package not found: " + arg + + " (missing __init__.py?)") else: - raise pytest.UsageError("file not found: " + arg) + raise UsageError("file not found: " + arg) parts[0] = path return parts @@ -732,11 +738,11 @@ class Session(FSCollector): nextnames = names[1:] resultnodes = [] for node in matching: - if isinstance(node, pytest.Item): + if isinstance(node, Item): if not names: resultnodes.append(node) continue - assert isinstance(node, pytest.Collector) + assert isinstance(node, Collector) rep = collect_one_node(node) if rep.passed: has_matched = False @@ -754,11 +760,11 @@ class Session(FSCollector): def genitems(self, node): self.trace("genitems", node) - if isinstance(node, pytest.Item): + if isinstance(node, Item): node.ihook.pytest_itemcollected(item=node) yield node else: - assert isinstance(node, pytest.Collector) + assert isinstance(node, Collector) rep = collect_one_node(node) if rep.passed: for subnode in rep.result: diff --git a/_pytest/monkeypatch.py b/_pytest/monkeypatch.py index 9151db3ad..ec846f910 100644 --- a/_pytest/monkeypatch.py +++ b/_pytest/monkeypatch.py @@ -1,17 +1,17 @@ """ monkeypatching and mocking functionality. """ from __future__ import absolute_import, division, print_function -import os, sys +import os +import sys import re from py.builtin import _basestring - -import pytest +from _pytest.fixtures import fixture RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$") -@pytest.fixture +@fixture def monkeypatch(): """The returned ``monkeypatch`` fixture provides these helper methods to modify objects, dictionaries or os.environ:: diff --git a/_pytest/python.py b/_pytest/python.py index 61000676e..985b476ca 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -9,13 +9,13 @@ import math from itertools import count import py -import pytest from _pytest.mark import MarkerError - +from _pytest.config import hookimpl import _pytest import _pytest._pluggy as pluggy from _pytest import fixtures +from _pytest import main from _pytest.compat import ( isclass, isfunction, is_generator, _escape_strings, REGEX_TYPE, STRING_TYPES, NoneType, NOTSET, @@ -50,7 +50,7 @@ def filter_traceback(entry): def pyobj_property(name): def get(self): - node = self.getparent(getattr(pytest, name)) + node = self.getparent(getattr(__import__('pytest'), name)) if node is not None: return node.obj doc = "python %s object this node was collected from (can be None)." % ( @@ -128,7 +128,7 @@ def pytest_configure(config): ) -@pytest.hookimpl(trylast=True) +@hookimpl(trylast=True) def pytest_pyfunc_call(pyfuncitem): testfunction = pyfuncitem.obj if pyfuncitem._isyieldedfunction(): @@ -141,6 +141,7 @@ def pytest_pyfunc_call(pyfuncitem): testfunction(**testargs) return True + def pytest_collect_file(path, parent): ext = path.ext if ext == ".py": @@ -156,7 +157,7 @@ def pytest_collect_file(path, parent): def pytest_pycollect_makemodule(path, parent): return Module(path, parent) -@pytest.hookimpl(hookwrapper=True) +@hookimpl(hookwrapper=True) def pytest_pycollect_makeitem(collector, name, obj): outcome = yield res = outcome.get_result() @@ -252,7 +253,7 @@ class PyobjMixin(PyobjContext): assert isinstance(lineno, int) return fspath, lineno, modpath -class PyCollector(PyobjMixin, pytest.Collector): +class PyCollector(PyobjMixin, main.Collector): def funcnamefilter(self, name): return self._matches_prefix_or_glob_option('python_functions', name) @@ -576,7 +577,7 @@ class FunctionMixin(PyobjMixin): entry.set_repr_style('short') def _repr_failure_py(self, excinfo, style="long"): - if excinfo.errisinstance(pytest.fail.Exception): + if excinfo.errisinstance(fail.Exception): if not excinfo.value.pytrace: return py._builtin._totext(excinfo.value) return super(FunctionMixin, self)._repr_failure_py(excinfo, @@ -774,7 +775,7 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): to set a dynamic scope using test context or configuration. """ from _pytest.fixtures import scope2index - from _pytest.mark import ParameterSet + from _pytest.mark import extract_argvalue, MARK_GEN from py.io import saferepr if not isinstance(argnames, (tuple, list)): @@ -1240,7 +1241,7 @@ class RaisesContext(object): def __exit__(self, *tp): __tracebackhide__ = True if tp[0] is None: - pytest.fail(self.message) + fail(self.message) if sys.version_info < (2, 7): # py26: on __exit__() exc_value often does not contain the # exception value. @@ -1511,7 +1512,7 @@ class ApproxNonIterable(object): # the basic pytest Function item # -class Function(FunctionMixin, pytest.Item, fixtures.FuncargnamesCompatAttr): +class Function(FunctionMixin, main.Item, fixtures.FuncargnamesCompatAttr): """ a Function Item is responsible for setting up and executing a Python test function. """ diff --git a/_pytest/recwarn.py b/_pytest/recwarn.py index 43d360736..73325cb8e 100644 --- a/_pytest/recwarn.py +++ b/_pytest/recwarn.py @@ -7,10 +7,11 @@ import _pytest._code import py import sys import warnings -import pytest + +from _pytest.fixtures import yield_fixture -@pytest.yield_fixture +@yield_fixture def recwarn(): """Return a WarningsRecorder instance that provides these methods: @@ -190,7 +191,8 @@ class WarningsChecker(WarningsRecorder): if not any(issubclass(r.category, self.expected_warning) for r in self): __tracebackhide__ = True - pytest.fail("DID NOT WARN. No warnings of type {0} was emitted. " - "The list of emitted warnings is: {1}.".format( - self.expected_warning, - [each.message for each in self])) + from _pytest.runner import fail + fail("DID NOT WARN. No warnings of type {0} was emitted. " + "The list of emitted warnings is: {1}.".format( + self.expected_warning, + [each.message for each in self])) diff --git a/_pytest/runner.py b/_pytest/runner.py index ee1683042..87dd240c8 100644 --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -6,7 +6,6 @@ import sys from time import time import py -import pytest from _pytest._code.code import TerminalRepr, ExceptionInfo @@ -257,7 +256,7 @@ def pytest_runtest_makereport(item, call): if not isinstance(excinfo, ExceptionInfo): outcome = "failed" longrepr = excinfo - elif excinfo.errisinstance(pytest.skip.Exception): + elif excinfo.errisinstance(skip.Exception): outcome = "skipped" r = excinfo._getreprcrash() longrepr = (str(r.path), r.lineno, r.message) diff --git a/_pytest/skipping.py b/_pytest/skipping.py index f2ee41518..c68cdea47 100644 --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -6,9 +6,9 @@ import sys import traceback import py -import pytest +from _pytest.config import hookimpl from _pytest.mark import MarkInfo, MarkDecorator - +from _pytest.runner import fail, skip def pytest_addoption(parser): group = parser.getgroup("general") @@ -25,6 +25,8 @@ def pytest_addoption(parser): def pytest_configure(config): if config.option.runxfail: + # yay a hack + import pytest old = pytest.xfail config._cleanup.append(lambda: setattr(pytest, "xfail", old)) @@ -57,7 +59,7 @@ def pytest_configure(config): ) -class XFailed(pytest.fail.Exception): +class XFailed(fail.Exception): """ raised from an explicit call to pytest.xfail() """ @@ -98,15 +100,15 @@ class MarkEvaluator(object): except Exception: self.exc = sys.exc_info() if isinstance(self.exc[1], SyntaxError): - msg = [" " * (self.exc[1].offset + 4) + "^",] + msg = [" " * (self.exc[1].offset + 4) + "^", ] msg.append("SyntaxError: invalid syntax") else: msg = 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) + fail("Error evaluating %r expression\n" + " %s\n" + "%s" + % (self.name, self.expr, "\n".join(msg)), + pytrace=False) def _getglobals(self): d = {'os': os, 'sys': sys, 'config': self.item.config} @@ -138,7 +140,7 @@ class MarkEvaluator(object): # XXX better be checked at collection time msg = "you need to specify reason=STRING " \ "when using booleans as conditions." - pytest.fail(msg) + fail(msg) result = bool(expr) if result: self.result = True @@ -162,7 +164,7 @@ class MarkEvaluator(object): return expl -@pytest.hookimpl(tryfirst=True) +@hookimpl(tryfirst=True) def pytest_runtest_setup(item): # Check if skip or skipif are specified as pytest marks @@ -171,23 +173,23 @@ def pytest_runtest_setup(item): eval_skipif = MarkEvaluator(item, 'skipif') if eval_skipif.istrue(): item._evalskip = eval_skipif - pytest.skip(eval_skipif.getexplanation()) + skip(eval_skipif.getexplanation()) skip_info = item.keywords.get('skip') if isinstance(skip_info, (MarkInfo, MarkDecorator)): item._evalskip = True if 'reason' in skip_info.kwargs: - pytest.skip(skip_info.kwargs['reason']) + skip(skip_info.kwargs['reason']) elif skip_info.args: - pytest.skip(skip_info.args[0]) + skip(skip_info.args[0]) else: - pytest.skip("unconditional skip") + skip("unconditional skip") item._evalxfail = MarkEvaluator(item, 'xfail') check_xfail_no_run(item) -@pytest.mark.hookwrapper +@hookimpl(hookwrapper=True) def pytest_pyfunc_call(pyfuncitem): check_xfail_no_run(pyfuncitem) outcome = yield @@ -217,7 +219,7 @@ def check_strict_xfail(pyfuncitem): pytest.fail('[XPASS(strict)] ' + explanation, pytrace=False) -@pytest.hookimpl(hookwrapper=True) +@hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield rep = outcome.get_result() @@ -237,7 +239,7 @@ def pytest_runtest_makereport(item, call): rep.wasxfail = rep.longrepr elif item.config.option.runxfail: pass # don't interefere - elif call.excinfo and call.excinfo.errisinstance(pytest.xfail.Exception): + elif call.excinfo and call.excinfo.errisinstance(xfail.Exception): rep.wasxfail = "reason: " + call.excinfo.value.msg rep.outcome = "skipped" elif evalxfail and not rep.skipped and evalxfail.wasvalid() and \