[Python-modules-commits] [pytest-catchlog] 01/02: Imported Upstream version 1.1

Daniel Stender danstender-guest at moszumanska.debian.org
Sun Jul 26 20:57:15 UTC 2015


This is an automated email from the git hooks/post-receive script.

danstender-guest pushed a commit to branch master
in repository pytest-catchlog.

commit 9f7b3008a9ac46b7780bbc50629e207b4ca365e9
Author: Daniel Stender <debian at danielstender.com>
Date:   Sun Jul 26 22:51:03 2015 +0200

    Imported Upstream version 1.1
---
 .gitignore              |  30 +++++++
 CHANGES.rst             |  23 ++++++
 LICENSE.txt             |  22 ++++++
 MANIFEST.in             |   4 +
 Makefile                |  36 +++++++++
 README.rst              | 127 +++++++++++++++++++++++++++++
 pytest_catchlog.py      | 206 ++++++++++++++++++++++++++++++++++++++++++++++++
 setup.cfg               |   5 ++
 setup.py                |  46 +++++++++++
 test_pytest_catchlog.py | 176 +++++++++++++++++++++++++++++++++++++++++
 tox.ini                 |   9 +++
 11 files changed, 684 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..15fa13f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,30 @@
+### Python template
+# Byte-compiled / optimized / Native Library Files
+__pycache__/
+*.py[cod]
+*.so
+
+# Distribution / packaging
+.Python
+/env/
+/build/
+/develop-eggs/
+/dist/
+/downloads/
+/eggs/
+/lib/
+/lib64/
+/parts/
+/sdist/
+/var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.cache
+nosetests.xml
+coverage.xml
diff --git a/CHANGES.rst b/CHANGES.rst
new file mode 100644
index 0000000..b7d938e
--- /dev/null
+++ b/CHANGES.rst
@@ -0,0 +1,23 @@
+Changelog
+=========
+
+List of notable changes between pytest-catchlog releases.
+
+Version 1.1
+-----------
+
+Released on 2015-06-07.
+
+- #2 - Explicitly state Python3 support and add configuration for running
+  tests with tox on multiple Python versions. (Thanks to Jeremy Bowman!)
+- Add an option to silence logs completely on the terminal.
+
+
+Version 1.0
+-----------
+
+Released on 2014-12-08.
+
+- Add ``record_tuples`` for comparing recorded log entries against expected
+  log entries with their logger name, severity and formatted message.
+
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..b4f4583
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,22 @@
+The MIT License
+
+Original work Copyright (c) 2010 Meme Dough
+Modified work Copyright (c) 2014 Arthur Skowronek
+
+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.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..8ad1455
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,4 @@
+include MANIFEST.in Makefile LICENSE.txt README.rst CHANGES.rst setup.cfg
+
+global-exclude *pyc
+prune __pycache__
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c664c1b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,36 @@
+.PHONY: docs
+.SILENT: init-devel test test-tox test-coverage
+
+all: clean test
+
+clean-coverage:
+	-rm .coverage*
+	-rm coverage.xml
+	-rm -rfv htmlcov
+
+clean-pyc:
+	-find . -path './.tox' -prune -or \
+		-name '__pycache__' -exec rm -rv {} +
+	-find . -path './.tox' -prune -or \
+		\( -name '*.pyc' -or -name '*.pyo' \) -exec rm -rv {} +
+
+clean: clean-pyc clean-coverage
+	-rm -rv build dist *.egg-info
+
+test:
+	py.test -v test_pytest_catchlog.py
+
+test-coverage:
+	coverage erase
+	coverage run --source=pytest_catchlog --branch -m pytest -v
+	coverage report
+	coverage xml
+
+audit:
+	flake8 pytest_catchlog.py
+
+wheel:
+	python setup.py bdist_wheel
+
+sdist:
+	python setup.py sdist
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..2287412
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,127 @@
+pytest-catchlog
+===============
+
+py.test plugin to catch log messages.  This is a fork of `pytest-capturelog`_.
+
+.. _`pytest-capturelog`: https://pypi.python.org/pypi/pytest-capturelog/
+
+
+Installation
+------------
+
+The `pytest-catchlog`_ package may be installed with pip or easy_install::
+
+    pip install pytest-catchlog
+    easy_install pytest-catchlog
+
+.. _`pytest-catchlog`: http://pypi.python.org/pypi/pytest-catchlog/
+
+
+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_pytest_catchlog.py
+
+Shows failed tests like so::
+
+    -------------------------- Captured log ---------------------------
+    test_pytest_catchlog.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_pytest_catchlog.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 reporting logs on failed tests
+completely with::
+
+    py.test --no-print-logs test_pytest_catchlog.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 catch 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.
+
+You can also resort to ``record_tuples`` if all you want to do is to ensure,
+that certain messages have been logged under a given logger name with a
+given severity and message::
+
+    def test_foo(caplog):
+        logging.getLogger().info('boo %s', 'arg')
+
+        assert caplog.record_tuples() == [
+            ('root', logging.INFO, 'boo arg'),
+        ]
+
diff --git a/pytest_catchlog.py b/pytest_catchlog.py
new file mode 100644
index 0000000..61cdb94
--- /dev/null
+++ b/pytest_catchlog.py
@@ -0,0 +1,206 @@
+# -*- coding: utf-8 -*-
+from __future__ import (absolute_import, print_function,
+                        unicode_literals, division)
+
+import logging
+
+import py
+
+
+__version__ = '1.1'
+
+
+def pytest_addoption(parser):
+    """Add options to control log capturing."""
+
+    group = parser.getgroup('catchlog', 'Log catching.')
+    group.addoption('--no-print-logs',
+                    dest='log_print', action='store_false', default=True,
+                    help='disable printing caught logs on failed tests.')
+    group.addoption('--log-format',
+                    dest='log_format',
+                    default=('%(filename)-25s %(lineno)4d'
+                             ' %(levelname)-8s %(message)s'),
+                    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):
+    """Always register the log catcher plugin with py.test or tests can't
+    find the  fixture function.
+    """
+    config.pluginmanager.register(CatchLogPlugin(config), '_catch_log')
+
+
+class CatchLogPlugin(object):
+    """Attaches to the logging module and captures log messages for each test.
+    """
+
+    def __init__(self, config):
+        """Creates a new plugin to capture log messages.
+
+        The formatter can be safely shared across all handlers so
+        create a single one for the entire test session here.
+        """
+        self.print_logs = config.getvalue('log_print')
+        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.catch_log_handler = CatchLogHandler()
+        item.catch_log_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.catch_log_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.catch_log_handler)
+
+            # For failed tests that have captured log messages add a
+            # captured log section to the report if desired.
+            if not report.passed and self.print_logs:
+                long_repr = getattr(report, 'longrepr', None)
+                if hasattr(long_repr, 'addsection'):
+                    log = item.catch_log_handler.stream.getvalue().strip()
+                    if log:
+                        long_repr.addsection('Captured log', log)
+
+            # Release the handler resources.
+            item.catch_log_handler.close()
+            del item.catch_log_handler
+
+        return report
+
+
+class CatchLogHandler(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 CatchLogFuncArg(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 record_tuples(self):
+        """Returns a list of a striped down version of log records intended
+        for use in assertion comparison.
+
+        The format of the tuple is:
+
+            (logger_name, log_level, message)
+        """
+        return [(r.name, r.levelno, r.getMessage()) for r in self.records()]
+
+    def set_level(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 at_level(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 CatchLogLevel(obj, level)
+
+
+class CatchLogLevel(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 CatchLogFuncArg(request._pyfuncitem.catch_log_handler)
+
+
+def pytest_funcarg__capturelog(request):
+    """Returns a funcarg to access and control log capturing."""
+
+    return CatchLogFuncArg(request._pyfuncitem.catch_log_handler)
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..ca1cfaf
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[wheel]
+universal = 1
+
+[sdist]
+formats = zip
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..ba84092
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+import io
+import os
+import re
+
+from setuptools import setup
+
+
+def _read_text_file(file_name):
+    file_path = os.path.join(os.path.dirname(__file__), file_name)
+    with io.open(file_path, encoding='utf-8') as f_stream:
+        return f_stream.read()
+
+
+def _get_version():
+    return re.search("__version__\s*=\s*'([^']+)'\s*",
+                     _read_text_file('pytest_catchlog.py')).group(1)
+
+
+setup(name='pytest-catchlog',
+      version=_get_version(),
+      description=('py.test plugin to catch log messages.'
+                   ' This is a fork of pytest-capturelog.'),
+      long_description=_read_text_file('README.rst'),
+      author='Arthur Skowronek (Fork Author)',  # original author: Meme Dough
+      author_email='eisensheng at mailbox.org',
+      url='https://github.com/eisensheng/pytest-catchlog',
+      py_modules=['pytest_catchlog', ],
+      install_requires=['py>=1.1.1', ],
+      entry_points={'pytest11': ['pytest_catchlog = pytest_catchlog']},
+      license='MIT License',
+      zip_safe=False,
+      keywords='py.test pytest',
+      classifiers=['Development Status :: 4 - Beta',
+                   'Intended Audience :: Developers',
+                   'License :: OSI Approved :: MIT License',
+                   'Operating System :: OS Independent',
+                   'Programming Language :: Python',
+                   'Programming Language :: Python :: 2.7',
+                   'Programming Language :: Python :: 3',
+                   'Programming Language :: Python :: 3.2',
+                   'Programming Language :: Python :: 3.3',
+                   'Programming Language :: Python :: 3.4',
+                   'Programming Language :: Python :: Implementation :: CPython',
+                   'Programming Language :: Python :: Implementation :: PyPy',
+                   'Topic :: Software Development :: Testing'])
diff --git a/test_pytest_catchlog.py b/test_pytest_catchlog.py
new file mode 100644
index 0000000..0e9d55f
--- /dev/null
+++ b/test_pytest_catchlog.py
@@ -0,0 +1,176 @@
+import py
+
+pytest_plugins = 'pytester', 'catchlog'
+
+
+def test_nothing_logged(testdir):
+    testdir.makepyfile('''
+        import sys
+        import logging
+
+        pytest_plugins = 'catchlog'
+
+        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 call -*',
+                                 'text going to stdout'])
+    result.stdout.fnmatch_lines(['*- Captured stderr call -*',
+                                 '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
+
+        pytest_plugins = 'catchlog'
+
+        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 call -*',
+                                 'text going to stdout'])
+    result.stdout.fnmatch_lines(['*- Captured stderr call -*',
+                                 'text going to stderr'])
+
+
+def test_change_level(testdir):
+    testdir.makepyfile('''
+        import sys
+        import logging
+
+        pytest_plugins = 'catchlog'
+
+        def test_foo(caplog):
+            caplog.set_level(logging.INFO)
+            log = logging.getLogger()
+            log.debug('handler DEBUG level')
+            log.info('handler INFO level')
+
+            caplog.set_level(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*'])
+
+
+ at py.test.mark.skipif('sys.version_info < (2,5)')
+def test_with_statement(testdir):
+    testdir.makepyfile('''
+        from __future__ import with_statement
+        import sys
+        import logging
+
+        pytest_plugins = 'catchlog'
+
+        def test_foo(caplog):
+            with caplog.at_level(logging.INFO):
+                log = logging.getLogger()
+                log.debug('handler DEBUG level')
+                log.info('handler INFO level')
+
+                with caplog.at_level(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
+
+        pytest_plugins = 'catchlog'
+
+        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*'])
+
+
+def test_record_tuples(testdir):
+    testdir.makepyfile('''
+        import sys
+        import logging
+
+        pytest_plugins = 'catchlog'
+
+        def test_foo(caplog):
+            logging.getLogger().info('boo %s', 'arg')
+
+            assert caplog.record_tuples() == [
+                ('root', logging.INFO, 'boo arg'),
+            ]
+        ''')
+    result = testdir.runpytest()
+    assert result.ret == 0
+
+
+def test_disable_log_capturing(testdir):
+    testdir.makepyfile('''
+        import sys
+        import logging
+
+        pytest_plugins = 'catchlog'
+
+        def test_foo(caplog):
+            sys.stdout.write('text going to stdout')
+            logging.getLogger().warning('catch me if you can!')
+            sys.stderr.write('text going to stderr')
+            assert False
+        ''')
+    result = testdir.runpytest('--no-print-logs')
+    print(result.stdout)
+    assert result.ret == 1
+    result.stdout.fnmatch_lines(['*- Captured stdout call -*',
+                                 'text going to stdout'])
+    result.stdout.fnmatch_lines(['*- Captured stderr call -*',
+                                 'text going to stderr'])
+    py.test.raises(Exception, result.stdout.fnmatch_lines,
+                   ['*- Captured log -*'])
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..3be9fcb
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,9 @@
+[tox]
+envlist = py{27,32,33,34,py,py3}
+
+[testenv]
+deps =
+    py==1.4.28
+    pytest==2.7.1
+commands =
+    py.test {posargs:test_pytest_catchlog.py}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/pytest-catchlog.git



More information about the Python-modules-commits mailing list