[Pytest-commit] commit/pytest: 3 new changesets
commits-noreply at bitbucket.org
commits-noreply at bitbucket.org
Fri Mar 14 08:11:48 CET 2014
3 new commits in pytest:
https://bitbucket.org/hpk42/pytest/commits/a016be612113/
Changeset: a016be612113
User: hpk42
Date: 2014-03-11 22:10:17
Summary: introduce warning system with this API:
- node.warn() for a node-specific warning
- config.warn() for a global non-node specific warning
Each warning is accompanied by a "warning number" so that we can later
introduce mechanisms for surpressing them.
Each warning will trigger a call to pytest_report_warn(number, node, message)
which is by default implemented by the TerminalReporter which introduces
a new option "-rw" to show details about warnings.
Affected #: 5 files
diff -r 8d2d44cf0345a9e4e5992768c50c5918b8dc08cc -r a016be6121137d7e9bf6c534a7070e5cf1ee267b _pytest/config.py
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -613,6 +613,11 @@
self.hook.pytest_unconfigure(config=self)
self.pluginmanager.ensure_shutdown()
+ def warn(self, code, message):
+ """ generate a warning for this test session. """
+ self.hook.pytest_logwarning(code=code, message=message,
+ fslocation=None, nodeid=None)
+
def pytest_cmdline_parse(self, pluginmanager, args):
assert self == pluginmanager.config, (self, pluginmanager.config)
self.parse(args)
diff -r 8d2d44cf0345a9e4e5992768c50c5918b8dc08cc -r a016be6121137d7e9bf6c534a7070e5cf1ee267b _pytest/hookspec.py
--- a/_pytest/hookspec.py
+++ b/_pytest/hookspec.py
@@ -227,6 +227,11 @@
def pytest_terminal_summary(terminalreporter):
""" add additional section in terminal summary reporting. """
+def pytest_logwarning(message, code, nodeid, fslocation):
+ """ process a warning specified by a message, a code string,
+ a nodeid and fslocation (both of which may be None
+ if the warning is not tied to a partilar node/location)."""
+
# -------------------------------------------------------------------------
# doctest hooks
# -------------------------------------------------------------------------
diff -r 8d2d44cf0345a9e4e5992768c50c5918b8dc08cc -r a016be6121137d7e9bf6c534a7070e5cf1ee267b _pytest/main.py
--- a/_pytest/main.py
+++ b/_pytest/main.py
@@ -263,6 +263,20 @@
return "<%s %r>" %(self.__class__.__name__,
getattr(self, 'name', None))
+ def warn(self, code, message):
+ """ generate a warning with the given code and message for this
+ item. """
+ assert isinstance(code, str)
+ fslocation = getattr(self, "location", None)
+ if fslocation is None:
+ fslocation = getattr(self, "fspath", None)
+ else:
+ fslocation = "%s:%s" % fslocation[:2]
+
+ self.ihook.pytest_logwarning(code=code, message=message,
+ nodeid=self.nodeid,
+ fslocation=fslocation)
+
# methods for ordering nodes
@property
def nodeid(self):
diff -r 8d2d44cf0345a9e4e5992768c50c5918b8dc08cc -r a016be6121137d7e9bf6c534a7070e5cf1ee267b _pytest/terminal.py
--- a/_pytest/terminal.py
+++ b/_pytest/terminal.py
@@ -75,6 +75,14 @@
letter = "f"
return report.outcome, letter, report.outcome.upper()
+class WarningReport:
+ def __init__(self, code, message, nodeid=None, fslocation=None):
+ self.code = code
+ self.message = message
+ self.nodeid = nodeid
+ self.fslocation = fslocation
+
+
class TerminalReporter:
def __init__(self, config, file=None):
self.config = config
@@ -151,6 +159,12 @@
self.write_line("INTERNALERROR> " + line)
return 1
+ def pytest_logwarning(self, code, fslocation, message, nodeid):
+ warnings = self.stats.setdefault("warnings", [])
+ warning = WarningReport(code=code, fslocation=fslocation,
+ message=message, nodeid=nodeid)
+ warnings.append(warning)
+
def pytest_plugin_registered(self, plugin):
if self.config.option.traceconfig:
msg = "PLUGIN registered: %s" % (plugin,)
@@ -335,6 +349,7 @@
self.summary_errors()
self.summary_failures()
self.summary_hints()
+ self.summary_warnings()
self.config.hook.pytest_terminal_summary(terminalreporter=self)
if exitstatus == 2:
self._report_keyboardinterrupt()
@@ -405,6 +420,16 @@
for hint in self.config.pluginmanager._hints:
self._tw.line("hint: %s" % hint)
+ def summary_warnings(self):
+ if self.hasopt("w"):
+ warnings = self.stats.get("warnings")
+ if not warnings:
+ return
+ self.write_sep("=", "warning summary")
+ for w in warnings:
+ self._tw.line("W%s %s %s" % (w.code,
+ w.fslocation, w.message))
+
def summary_failures(self):
if self.config.option.tbstyle != "no":
reports = self.getreports('failed')
@@ -449,7 +474,8 @@
def summary_stats(self):
session_duration = py.std.time.time() - self._sessionstarttime
- keys = "failed passed skipped deselected xfailed xpassed".split()
+ keys = ("failed passed skipped deselected "
+ "xfailed xpassed warnings").split()
for key in self.stats.keys():
if key not in keys:
keys.append(key)
diff -r 8d2d44cf0345a9e4e5992768c50c5918b8dc08cc -r a016be6121137d7e9bf6c534a7070e5cf1ee267b testing/test_config.py
--- a/testing/test_config.py
+++ b/testing/test_config.py
@@ -360,4 +360,43 @@
assert l[-2] == m.pytest_load_initial_conftests
assert l[-3].__module__ == "_pytest.config"
+class TestWarning:
+ def test_warn_config(self, testdir):
+ testdir.makeconftest("""
+ l = []
+ def pytest_configure(config):
+ config.warn("C1", "hello")
+ def pytest_logwarning(code, message):
+ assert code == "C1"
+ assert message == "hello"
+ l.append(1)
+ """)
+ testdir.makepyfile("""
+ def test_proper(pytestconfig):
+ import conftest
+ assert conftest.l == [1]
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome(passed=1)
+ def test_warn_on_test_item_from_request(self, testdir):
+ testdir.makepyfile("""
+ import pytest
+
+ @pytest.fixture
+ def fix(request):
+ request.node.warn("T1", "hello")
+ def test_hello(fix):
+ pass
+ """)
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines("""
+ *1 warning*
+ """)
+ assert "hello" not in result.stdout.str()
+ result = testdir.runpytest("-rw")
+ result.stdout.fnmatch_lines("""
+ ===*warning summary*===
+ *WT1*test_warn_on_test_item*:5*hello*
+ *1 warning*
+ """)
https://bitbucket.org/hpk42/pytest/commits/a1ff8ba50ac5/
Changeset: a1ff8ba50ac5
User: hpk42
Date: 2014-03-11 22:10:18
Summary: warn if instances are callable and have a test name
Affected #: 3 files
diff -r a016be6121137d7e9bf6c534a7070e5cf1ee267b -r a1ff8ba50ac5617a2151a162f31887de7e48f761 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -11,7 +11,8 @@
NoneType = type(None)
NOTSET = object()
-
+isfunction = inspect.isfunction
+isclass = inspect.isclass
callable = py.builtin.callable
def getfslineno(obj):
@@ -44,7 +45,7 @@
self.ids = ids
def __call__(self, function):
- if inspect.isclass(function):
+ if isclass(function):
raise ValueError(
"class fixtures not supported (may be in the future)")
function._pytestfixturefunction = self
@@ -213,14 +214,19 @@
res = __multicall__.execute()
if res is not None:
return res
- if inspect.isclass(obj):
+ if isclass(obj):
#if hasattr(collector.obj, 'unittest'):
# return # we assume it's a mixin class for a TestCase derived one
if collector.classnamefilter(name):
Class = collector._getcustomclass("Class")
return Class(name, parent=collector)
- elif collector.funcnamefilter(name) and hasattr(obj, '__call__') and \
+ elif collector.funcnamefilter(name) and hasattr(obj, "__call__") and \
getfixturemarker(obj) is None:
+ if not isfunction(obj):
+ collector.warn(code="C2", message=
+ "cannot collect %r because it is not a function."
+ % name, )
+ return
if is_generator(obj):
return Generator(name, parent=collector)
else:
@@ -498,10 +504,9 @@
""" Collector for test methods. """
def collect(self):
if hasinit(self.obj):
- pytest.skip("class %s.%s with __init__ won't get collected" % (
- self.obj.__module__,
- self.obj.__name__,
- ))
+ self.warn("C1", "cannot collect test class %r because it has a "
+ "__init__ constructor" % self.obj.__name__)
+ return []
return [self._getcustomclass("Instance")(name="()", parent=self)]
def setup(self):
diff -r a016be6121137d7e9bf6c534a7070e5cf1ee267b -r a1ff8ba50ac5617a2151a162f31887de7e48f761 _pytest/terminal.py
--- a/_pytest/terminal.py
+++ b/_pytest/terminal.py
@@ -15,7 +15,7 @@
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.")
+ "(E)error, (s)skipped, (x)failed, (X)passed (w)warnings.")
group._addoption('-l', '--showlocals',
action="store_true", dest="showlocals", default=False,
help="show locals in tracebacks (disabled by default).")
diff -r a016be6121137d7e9bf6c534a7070e5cf1ee267b -r a1ff8ba50ac5617a2151a162f31887de7e48f761 testing/python/collect.py
--- a/testing/python/collect.py
+++ b/testing/python/collect.py
@@ -33,8 +33,8 @@
pytest.raises(ImportError, lambda: modcol.obj)
class TestClass:
- def test_class_with_init_skip_collect(self, testdir):
- modcol = testdir.getmodulecol("""
+ def test_class_with_init_warning(self, testdir):
+ testdir.makepyfile("""
class TestClass1:
def __init__(self):
pass
@@ -42,11 +42,11 @@
def __init__(self):
pass
""")
- l = modcol.collect()
- assert len(l) == 2
-
- for classcol in l:
- pytest.raises(pytest.skip.Exception, classcol.collect)
+ result = testdir.runpytest("-rw")
+ result.stdout.fnmatch_lines("""
+ WC1*test_class_with_init_warning.py*__init__*
+ *2 warnings*
+ """)
def test_class_subclassobject(self, testdir):
testdir.getmodulecol("""
@@ -276,6 +276,17 @@
assert isinstance(modcol, pytest.Module)
assert hasattr(modcol.obj, 'test_func')
+ def test_function_as_object_instance_ignored(self, testdir):
+ item = testdir.makepyfile("""
+ class A:
+ def __call__(self, tmpdir):
+ 0/0
+
+ test_a = A()
+ """)
+ reprec = testdir.inline_run()
+ reprec.assertoutcome()
+
def test_function_equality(self, testdir, tmpdir):
from _pytest.python import FixtureManager
config = testdir.parseconfigure()
https://bitbucket.org/hpk42/pytest/commits/e18da3213547/
Changeset: e18da3213547
User: hpk42
Date: 2014-03-11 22:10:51
Summary: shrink and merge the somewhat obscure and undocumented internal hinting
system with the new warnings one
Affected #: 6 files
diff -r a1ff8ba50ac5617a2151a162f31887de7e48f761 -r e18da321354757558ffd286a07e94d2b1cd42b18 _pytest/config.py
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -82,6 +82,9 @@
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.")
+ while self._warnings:
+ warning = self._warnings.pop(0)
+ config.warn(code="I1", message=warning)
class Parser:
@@ -94,7 +97,6 @@
self._usage = usage
self._inidict = {}
self._ininames = []
- self.hints = []
def processoption(self, option):
if self._processopt:
@@ -379,14 +381,6 @@
py.std.argparse.ArgumentParser.__init__(self, usage=parser._usage,
add_help=False, formatter_class=DropShorterLongHelpFormatter)
- 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 ""
-
def parse_args(self, args=None, namespace=None):
"""allow splitting of positional arguments"""
args, argv = self.parse_known_args(args, namespace)
@@ -716,7 +710,6 @@
self._preparse(args)
# XXX deprecated hook:
self.hook.pytest_cmdline_preparse(config=self, args=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())
diff -r a1ff8ba50ac5617a2151a162f31887de7e48f761 -r e18da321354757558ffd286a07e94d2b1cd42b18 _pytest/core.py
--- a/_pytest/core.py
+++ b/_pytest/core.py
@@ -71,7 +71,7 @@
self._name2plugin = {}
self._listattrcache = {}
self._plugins = []
- self._hints = []
+ self._warnings = []
self.trace = TagTracer().get("pluginmanage")
self._plugin_distinfo = []
self._shutdown = []
@@ -225,7 +225,7 @@
raise
elif not isinstance(e, py.test.skip.Exception):
raise
- self._hints.append("skipped plugin %r: %s" %((modname, e.msg)))
+ self._warnings.append("skipped plugin %r: %s" %((modname, e.msg)))
else:
self.register(mod, modname)
self.consider_module(mod)
diff -r a1ff8ba50ac5617a2151a162f31887de7e48f761 -r e18da321354757558ffd286a07e94d2b1cd42b18 _pytest/helpconfig.py
--- a/_pytest/helpconfig.py
+++ b/_pytest/helpconfig.py
@@ -64,7 +64,6 @@
def showhelp(config):
tw = py.io.TerminalWriter()
tw.write(config._parser.optparser.format_help())
- tw.write(config._parser.optparser.format_epilog(None))
tw.line()
tw.line()
#tw.sep( "=", "config file settings")
@@ -86,6 +85,8 @@
tw.line("to see available fixtures type: py.test --fixtures")
tw.line("(shown according to specified file_or_dir or current dir "
"if not specified)")
+ for warning in config.pluginmanager._warnings:
+ tw.line("warning: %s" % (warning,))
return
tw.line("conftest.py options:")
diff -r a1ff8ba50ac5617a2151a162f31887de7e48f761 -r e18da321354757558ffd286a07e94d2b1cd42b18 _pytest/terminal.py
--- a/_pytest/terminal.py
+++ b/_pytest/terminal.py
@@ -348,7 +348,6 @@
if exitstatus in (0, 1, 2, 4):
self.summary_errors()
self.summary_failures()
- self.summary_hints()
self.summary_warnings()
self.config.hook.pytest_terminal_summary(terminalreporter=self)
if exitstatus == 2:
@@ -415,11 +414,6 @@
l.append(x)
return l
- def summary_hints(self):
- if self.config.option.traceconfig:
- for hint in self.config.pluginmanager._hints:
- self._tw.line("hint: %s" % hint)
-
def summary_warnings(self):
if self.hasopt("w"):
warnings = self.stats.get("warnings")
diff -r a1ff8ba50ac5617a2151a162f31887de7e48f761 -r e18da321354757558ffd286a07e94d2b1cd42b18 testing/test_core.py
--- a/testing/test_core.py
+++ b/testing/test_core.py
@@ -43,11 +43,11 @@
""")
p.copy(p.dirpath("skipping2.py"))
monkeypatch.setenv("PYTEST_PLUGINS", "skipping2")
- result = testdir.runpytest("-p", "skipping1", "--traceconfig")
+ result = testdir.runpytest("-rw", "-p", "skipping1", "--traceconfig")
assert result.ret == 0
result.stdout.fnmatch_lines([
- "*hint*skipping1*hello*",
- "*hint*skipping2*hello*",
+ "WI1*skipped plugin*skipping1*hello*",
+ "WI1*skipped plugin*skipping2*hello*",
])
def test_consider_env_plugin_instantiation(self, testdir, monkeypatch):
diff -r a1ff8ba50ac5617a2151a162f31887de7e48f761 -r e18da321354757558ffd286a07e94d2b1cd42b18 testing/test_parseopt.py
--- a/testing/test_parseopt.py
+++ b/testing/test_parseopt.py
@@ -236,16 +236,6 @@
help = parser.optparser.format_help()
assert '-doit, --func-args foo' in help
- at pytest.mark.skipif("sys.version_info < (2,5)")
-def test_addoption_parser_epilog(testdir):
- testdir.makeconftest("""
- def pytest_addoption(parser):
- parser.hints.append("hello world")
- parser.hints.append("from me too")
- """)
- result = testdir.runpytest('--help')
- #assert result.ret != 0
- result.stdout.fnmatch_lines(["hint: hello world", "hint: from me too"])
@pytest.mark.skipif("sys.version_info < (2,6)")
def test_argcomplete(testdir, monkeypatch):
Repository URL: https://bitbucket.org/hpk42/pytest/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
More information about the pytest-commit
mailing list