2 new commits in pytest:

https://bitbucket.org/hpk42/pytest/commits/dfff965c16cb/
Changeset:   dfff965c16cb
Branch:      copy-in-cache
User:        RonnyPfannschmidt
Date:        2015-02-19 18:04:06+00:00
Summary:     just copy in capturelog with minimal changes
Affected #:  4 files

diff -r bc2cf073d49b5eaa8a403c2e1409e98f450d2ea5 -r 
dfff965c16cb8ad509b0cd16e2930081d628dbc1 _pytest/capture_log.py
--- /dev/null
+++ b/_pytest/capture_log.py
@@ -0,0 +1,301 @@
+"""capture output of logging module.
+
+Installation
+------------
+
+The `pytest-capturelog`_ package may be installed with pip or easy_install::
+
+    pip install pytest-capturelog
+    easy_install pytest-capturelog
+
+.. _`pytest-capturelog`: http://pypi.python.org/pypi/pytest-capturelog/
+
+Usage
+-----
+
+If the plugin is installed log messages are captured by default and for
+each failed test will be shown in the same manner as captured stdout and
+stderr.
+
+Running without options::
+
+    py.test test_capturelog.py
+
+Shows failed tests like so::
+
+    -------------------------- Captured log ---------------------------
+    test_capturelog.py          26 INFO     text going to logger
+    ------------------------- Captured stdout -------------------------
+    text going to stdout
+    ------------------------- Captured stderr -------------------------
+    text going to stderr
+    ==================== 2 failed in 0.02 seconds =====================
+
+By default each captured log message shows the module, line number,
+log level and message.  Showing the exact module and line number is
+useful for testing and debugging.  If desired the log format and date
+format can be specified to anything that the logging module supports.
+
+Running pytest specifying formatting options::
+
+    py.test \
+        --log-format="%(asctime)s %(levelname)s %(message)s" \
+        --log-date-format="%Y-%m-%d %H:%M:%S" test_capturelog.py
+
+Shows failed tests like so::
+
+    -------------------------- Captured log ---------------------------
+    2010-04-10 14:48:44 INFO text going to logger
+    ------------------------- Captured stdout -------------------------
+    text going to stdout
+    ------------------------- Captured stderr -------------------------
+    text going to stderr
+    ==================== 2 failed in 0.02 seconds =====================
+
+Further it is possible to disable capturing of logs completely with::
+
+    py.test --nocapturelog test_capturelog.py
+
+Shows failed tests in the normal manner as no logs were captured::
+
+    ------------------------- Captured stdout -------------------------
+    text going to stdout
+    ------------------------- Captured stderr -------------------------
+    text going to stderr
+    ==================== 2 failed in 0.02 seconds =====================
+
+Inside tests it is possible to change the log level for the captured
+log messages.  This is supported by the ``caplog`` funcarg::
+
+    def test_foo(caplog):
+        caplog.setLevel(logging.INFO)
+        pass
+
+By default the level is set on the handler used to capture the log
+messages, however as a convenience it is also possible to set the log
+level of any logger::
+
+    def test_foo(caplog):
+        caplog.setLevel(logging.CRITICAL, logger='root.baz')
+        pass
+
+It is also possible to use a context manager to temporarily change the
+log level::
+
+    def test_bar(caplog):
+        with caplog.atLevel(logging.INFO):
+            pass
+
+Again, by default the level of the handler is affected but the level
+of any logger can be changed instead with::
+
+    def test_bar(caplog):
+        with caplog.atLevel(logging.CRITICAL, logger='root.baz'):
+            pass
+
+Lastly all the logs sent to the logger during the test run are made
+available on the funcarg in the form of both the LogRecord instances
+and the final log text.  This is useful for when you want to assert on
+the contents of a message::
+
+    def test_baz(caplog):
+        func_under_test()
+        for record in caplog.records():
+            assert record.levelname != 'CRITICAL'
+        assert 'wally' not in caplog.text()
+
+For all the available attributes of the log records see the
+``logging.LogRecord`` class.
+"""
+
+import py
+import logging
+
+LOG_FORMAT = '%(filename)-25s %(lineno)4d %(levelname)-8s %(message)s'
+
+
+def pytest_addoption(parser):
+    """Add options to control log capturing."""
+
+    group = parser.getgroup('capturelog', 'log capturing')
+    group.addoption('--nocapturelog',
+                    dest='capturelog',
+                    action='store_false',
+                    default=True,
+                    help='disable log capture')
+    group.addoption('--log-format',
+                    dest='log_format',
+                    default=LOG_FORMAT,
+                    help='log format as used by the logging module')
+    group.addoption('--log-date-format',
+                    dest='log_date_format',
+                    default=None,
+                    help='log date format as used by the logging module')
+
+
+def pytest_configure(config):
+    """Activate log capturing if appropriate."""
+
+    if config.getvalue('capturelog'):
+        config.pluginmanager.register(CaptureLogPlugin(config), '_capturelog')
+
+
+class CaptureLogPlugin(object):
+    """Attaches to the logging module and captures log messages for each 
test."""
+
+    def __init__(self, config):
+        """Creates a new plugin to capture log messges.
+
+        The formatter can be safely shared across all handlers so
+        create a single one for the entire test session here.
+        """
+
+        self.formatter = logging.Formatter(config.getvalue('log_format'),
+                                           config.getvalue('log_date_format'))
+
+    def pytest_runtest_setup(self, item):
+        """Start capturing log messages for this test.
+
+        Creating a specific handler for each test ensures that we
+        avoid multi threading issues.
+
+        Attaching the handler and setting the level at the beginning
+        of each test ensures that we are setup to capture log
+        messages.
+        """
+
+        # Create a handler for this test.
+        item.capturelog_handler = CaptureLogHandler()
+        item.capturelog_handler.setFormatter(self.formatter)
+
+        # Attach the handler to the root logger and ensure that the
+        # root logger is set to log all levels.
+        root_logger = logging.getLogger()
+        root_logger.addHandler(item.capturelog_handler)
+        root_logger.setLevel(logging.NOTSET)
+
+    def pytest_runtest_makereport(self, __multicall__, item, call):
+        """Add captured log messages for this report."""
+
+        report = __multicall__.execute()
+
+        # This fn called after setup, call and teardown.  Only
+        # interested in just after test call has finished.
+        if call.when == 'call':
+
+            # Detach the handler from the root logger to ensure no
+            # further access to the handler.
+            root_logger = logging.getLogger()
+            root_logger.removeHandler(item.capturelog_handler)
+
+            # For failed tests that have captured log messages add a
+            # captured log section to the report.
+            if not report.passed:
+                longrepr = getattr(report, 'longrepr', None)
+                if hasattr(longrepr, 'addsection'):
+                    log = item.capturelog_handler.stream.getvalue().strip()
+                    if log:
+                        longrepr.addsection('Captured log', log)
+
+            # Release the handler resources.
+            item.capturelog_handler.close()
+            del item.capturelog_handler
+
+        return report
+
+
+class CaptureLogHandler(logging.StreamHandler):
+    """A logging handler that stores log records and the log text."""
+
+    def __init__(self):
+        """Creates a new log handler."""
+
+        logging.StreamHandler.__init__(self)
+        self.stream = py.io.TextIO()
+        self.records  = []
+
+    def close(self):
+        """Close this log handler and its underlying stream."""
+
+        logging.StreamHandler.close(self)
+        self.stream.close()
+
+    def emit(self, record):
+        """Keep the log records in a list in addition to the log text."""
+
+        self.records.append(record)
+        logging.StreamHandler.emit(self, record)
+
+
+class CaptureLogFuncArg(object):
+    """Provides access and control of log capturing."""
+
+    def __init__(self, handler):
+        """Creates a new funcarg."""
+
+        self.handler = handler
+
+    def text(self):
+        """Returns the log text."""
+
+        return self.handler.stream.getvalue()
+
+    def records(self):
+        """Returns the list of log records."""
+
+        return self.handler.records
+
+    def setLevel(self, level, logger=None):
+        """Sets the level for capturing of logs.
+
+        By default, the level is set on the handler used to capture
+        logs. Specify a logger name to instead set the level of any
+        logger.
+        """
+
+        obj = logger and logging.getLogger(logger) or self.handler
+        obj.setLevel(level)
+
+    def atLevel(self, level, logger=None):
+        """Context manager that sets the level for capturing of logs.
+
+        By default, the level is set on the handler used to capture
+        logs. Specify a logger name to instead set the level of any
+        logger.
+        """
+
+        obj = logger and logging.getLogger(logger) or self.handler
+        return CaptureLogLevel(obj, level)
+
+
+class CaptureLogLevel(object):
+    """Context manager that sets the logging level of a handler or logger."""
+
+    def __init__(self, obj, level):
+        """Creates a new log level context manager."""
+
+        self.obj = obj
+        self.level = level
+
+    def __enter__(self):
+        """Adjust the log level."""
+
+        self.orig_level = self.obj.level
+        self.obj.setLevel(self.level)
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        """Restore the log level."""
+
+        self.obj.setLevel(self.orig_level)
+
+
+def pytest_funcarg__caplog(request):
+    """Returns a funcarg to access and control log capturing."""
+
+    return CaptureLogFuncArg(request._pyfuncitem.capturelog_handler)
+
+
+def pytest_funcarg__capturelog(request):
+    """Returns a funcarg to access and control log capturing."""
+
+    return CaptureLogFuncArg(request._pyfuncitem.capturelog_handler)

diff -r bc2cf073d49b5eaa8a403c2e1409e98f450d2ea5 -r 
dfff965c16cb8ad509b0cd16e2930081d628dbc1 _pytest/config.py
--- a/_pytest/config.py
+++ b/_pytest/config.py
@@ -51,7 +51,7 @@
 default_plugins = (
      "mark main terminal runner python pdb unittest capture skipping "
      "tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
-     "junitxml resultlog doctest cache").split()
+     "junitxml resultlog doctest cache capture_log").split()
 
 def _preloadplugins():
     assert not _preinit

diff -r bc2cf073d49b5eaa8a403c2e1409e98f450d2ea5 -r 
dfff965c16cb8ad509b0cd16e2930081d628dbc1 testing/test_capture.py
--- a/testing/test_capture.py
+++ b/testing/test_capture.py
@@ -337,6 +337,7 @@
             # verify proper termination
             assert "closed" not in s
 
+    @pytest.mark.xfail(reason='logcapture integration')
     def test_logging_initialized_in_test(self, testdir):
         p = testdir.makepyfile("""
             import sys

diff -r bc2cf073d49b5eaa8a403c2e1409e98f450d2ea5 -r 
dfff965c16cb8ad509b0cd16e2930081d628dbc1 testing/test_capturelog.py
--- /dev/null
+++ b/testing/test_capturelog.py
@@ -0,0 +1,106 @@
+import py
+
+def test_nothing_logged(testdir):
+    testdir.makepyfile('''
+        import sys
+        import logging
+
+
+        def test_foo():
+            sys.stdout.write('text going to stdout')
+            sys.stderr.write('text going to stderr')
+            assert False
+        ''')
+    result = testdir.runpytest()
+    assert result.ret == 1
+    result.stdout.fnmatch_lines(['* Captured stdout *', 'text going to 
stdout'])
+    result.stdout.fnmatch_lines(['* Captured stderr *', 'text going to 
stderr'])
+    py.test.raises(Exception, result.stdout.fnmatch_lines, ['* Captured log 
*'])
+
+def test_messages_logged(testdir):
+    testdir.makepyfile('''
+        import sys
+        import logging
+
+
+        def test_foo():
+            sys.stdout.write('text going to stdout')
+            sys.stderr.write('text going to stderr')
+            logging.getLogger().info('text going to logger')
+            assert False
+        ''')
+    result = testdir.runpytest()
+    assert result.ret == 1
+    result.stdout.fnmatch_lines(['* Captured log *', '*text going to logger*'])
+    result.stdout.fnmatch_lines(['* Captured stdout *', 'text going to 
stdout'])
+    result.stdout.fnmatch_lines(['* Captured stderr *', 'text going to 
stderr'])
+
+def test_change_level(testdir):
+    testdir.makepyfile('''
+        import sys
+        import logging
+
+
+        def test_foo(caplog):
+            caplog.setLevel(logging.INFO)
+            log = logging.getLogger()
+            log.debug('handler DEBUG level')
+            log.info('handler INFO level')
+
+            caplog.setLevel(logging.CRITICAL, logger='root.baz')
+            log = logging.getLogger('root.baz')
+            log.warning('logger WARNING level')
+            log.critical('logger CRITICAL level')
+
+            assert False
+        ''')
+    result = testdir.runpytest()
+    assert result.ret == 1
+    result.stdout.fnmatch_lines(['* Captured log *', '*handler INFO level*', 
'*logger CRITICAL level*'])
+    py.test.raises(Exception, result.stdout.fnmatch_lines, ['* Captured log 
*', '*handler DEBUG level*'])
+    py.test.raises(Exception, result.stdout.fnmatch_lines, ['* Captured log 
*', '*logger WARNING level*'])
+
+def test_with_statement(testdir):
+    testdir.makepyfile('''
+        from __future__ import with_statement
+        import sys
+        import logging
+
+
+        def test_foo(caplog):
+            with caplog.atLevel(logging.INFO):
+                log = logging.getLogger()
+                log.debug('handler DEBUG level')
+                log.info('handler INFO level')
+
+                with caplog.atLevel(logging.CRITICAL, logger='root.baz'):
+                    log = logging.getLogger('root.baz')
+                    log.warning('logger WARNING level')
+                    log.critical('logger CRITICAL level')
+
+            assert False
+        ''')
+    result = testdir.runpytest()
+    assert result.ret == 1
+    result.stdout.fnmatch_lines(['* Captured log *', '*handler INFO level*', 
'*logger CRITICAL level*'])
+    py.test.raises(Exception, result.stdout.fnmatch_lines, ['* Captured log 
*', '*handler DEBUG level*'])
+    py.test.raises(Exception, result.stdout.fnmatch_lines, ['* Captured log 
*', '*logger WARNING level*'])
+
+def test_log_access(testdir):
+    testdir.makepyfile('''
+        import sys
+        import logging
+
+
+        def test_foo(caplog):
+            logging.getLogger().info('boo %s', 'arg')
+            assert caplog.records()[0].levelname == 'INFO'
+            assert caplog.records()[0].msg == 'boo %s'
+            assert 'boo arg' in caplog.text()
+        ''')
+    result = testdir.runpytest()
+    assert result.ret == 0
+
+def test_funcarg_help(testdir):
+    result = testdir.runpytest('--funcargs')
+    result.stdout.fnmatch_lines(['*caplog*'])


https://bitbucket.org/hpk42/pytest/commits/8211b20606ae/
Changeset:   8211b20606ae
Branch:      copy-in-cache
User:        RonnyPfannschmidt
Date:        2015-02-19 20:29:52+00:00
Summary:     ignore the pytest-cache/capturelog distributions and fix related 
tests
Affected #:  4 files

diff -r dfff965c16cb8ad509b0cd16e2930081d628dbc1 -r 
8211b20606ae19bc1b71144e623c9bd5964773f2 _pytest/core.py
--- a/_pytest/core.py
+++ b/_pytest/core.py
@@ -10,7 +10,11 @@
 assert py.__version__.split(".")[:2] >= ['1', '4'], ("installation problem: "
     "%s is too old, remove or upgrade 'py'" % (py.__version__))
 
-py3 = sys.version_info > (3,0)
+py3 = sys.version_info > (3, 0)
+
+#
+_ALREADY_INCLUDED_PLUGINS = 'pytest-cache', 'pytest-capturelog'
+
 
 class TagTracer:
     def __init__(self):
@@ -269,6 +273,11 @@
         except ImportError:
             return # XXX issue a warning
         for ep in iter_entry_points('pytest11'):
+            project_name = getattr(ep.dist, 'project_name', None)
+            if project_name in _ALREADY_INCLUDED_PLUGINS:
+                self._warnings.append(
+                    'ignoring now included plugin ' + project_name)
+                continue
             name = ep.name
             if name.startswith("pytest_"):
                 name = name[7:]

diff -r dfff965c16cb8ad509b0cd16e2930081d628dbc1 -r 
8211b20606ae19bc1b71144e623c9bd5964773f2 testing/test_config.py
--- a/testing/test_config.py
+++ b/testing/test_config.py
@@ -307,6 +307,7 @@
         assert name == "pytest11"
         class EntryPoint:
             name = "mytestplugin"
+            dist = None
             def load(self):
                 assert 0, "should not arrive here"
         return iter([EntryPoint()])

diff -r dfff965c16cb8ad509b0cd16e2930081d628dbc1 -r 
8211b20606ae19bc1b71144e623c9bd5964773f2 testing/test_core.py
--- a/testing/test_core.py
+++ b/testing/test_core.py
@@ -70,12 +70,13 @@
             assert name == "pytest11"
             class EntryPoint:
                 name = "pytest_mytestplugin"
-                dist = None
+                class dist:
+                    name = None
                 def load(self):
                     class PseudoPlugin:
                         x = 42
                     return PseudoPlugin()
-            return iter([EntryPoint()])
+            yield EntryPoint()
 
         monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
         pluginmanager = PluginManager()
@@ -83,6 +84,28 @@
         plugin = pluginmanager.getplugin("mytestplugin")
         assert plugin.x == 42
 
+    @pytest.mark.parametrize('distname', ['pytest-cache', 'pytest-capturelog'])
+    def test_dont_consider_setuptools_included(self, distname, monkeypatch):
+        pkg_resources = pytest.importorskip("pkg_resources")
+        def my_iter(name):
+            assert name == "pytest11"
+            class EntryPoint:
+                name = "pytest_mytestplugin"
+                class dist:
+                    project_name = distname
+                def load(self):
+                    class PseudoPlugin:
+                        x = 42
+                    return PseudoPlugin()
+            yield EntryPoint()
+
+        monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
+        pluginmanager = PluginManager()
+        pluginmanager.consider_setuptools_entrypoints()
+        plugin = pluginmanager.getplugin("mytestplugin")
+        assert plugin is None
+
+
     def test_consider_setuptools_not_installed(self, monkeypatch):
         monkeypatch.setitem(py.std.sys.modules, 'pkg_resources',
             py.std.types.ModuleType("pkg_resources"))

diff -r dfff965c16cb8ad509b0cd16e2930081d628dbc1 -r 
8211b20606ae19bc1b71144e623c9bd5964773f2 tox.ini
--- a/tox.ini
+++ b/tox.ini
@@ -15,8 +15,9 @@
 
 [testenv:flakes]
 changedir=
-deps = pytest-flakes>=0.2
-commands = py.test --flakes -m flakes _pytest testing
+deps = flake8
+       mccabe
+commands = pytest.py _pytest testing
 
 [testenv:py27-xdist]
 changedir=.
@@ -141,5 +142,8 @@
 python_files=test_*.py *_test.py testing/*/*.py
 python_classes=Test Acceptance
 python_functions=test
-pep8ignore = E401 E225 E261 E128 E124 E302
 norecursedirs = .tox ja .hg
+
+
+[flake8]
+ignore = E401 E225 E261 E128 E124 E302

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.
_______________________________________________
pytest-commit mailing list
pytest-commit@python.org
https://mail.python.org/mailman/listinfo/pytest-commit

Reply via email to