[Python-modules-commits] [pytest-catchlog] 01/08: New upstream version 1.2.2+git20170915
Gianfranco Costamagna
locutusofborg at moszumanska.debian.org
Fri Sep 15 17:04:58 UTC 2017
This is an automated email from the git hooks/post-receive script.
locutusofborg pushed a commit to branch debian/master
in repository pytest-catchlog.
commit 23e29cf8fdd95222c54f25c3c646f1b5f52efcce
Author: Gianfranco Costamagna <costamagnagianfranco at yahoo.it>
Date: Fri Sep 15 18:42:01 2017 +0200
New upstream version 1.2.2+git20170915
---
.gitignore | 1 +
.travis.yml | 28 ++-
MANIFEST.in | 2 +-
Makefile | 4 +-
README.rst | 69 +++++-
pytest_catchlog.py | 313 --------------------------
pytest_catchlog/__init__.py | 2 +
pytest_catchlog/common.py | 66 ++++++
pytest_catchlog/fixture.py | 154 +++++++++++++
pytest_catchlog/plugin.py | 264 ++++++++++++++++++++++
setup.py | 9 +-
tasks.py | 2 +-
test_pytest_catchlog.py | 300 -------------------------
tests/conftest.py | 33 +++
tests/perf/__init__.py | 0
tests/perf/bench/conftest.py | 17 ++
tests/perf/bench/pytest.ini | 11 +
tests/perf/bench/test_log.py | 11 +
tests/perf/bench/test_runtest_hook.py | 47 ++++
tests/perf/conftest.py | 224 +++++++++++++++++++
tests/perf/data.py | 85 ++++++++
tests/perf/plot.py | 85 ++++++++
tests/perf/test_perf_run.py | 98 +++++++++
tests/test_compat.py | 80 +++++++
tests/test_fixture.py | 99 +++++++++
tests/test_reporting.py | 398 ++++++++++++++++++++++++++++++++++
tox.ini | 14 +-
27 files changed, 1779 insertions(+), 637 deletions(-)
diff --git a/.gitignore b/.gitignore
index 15fa13f..1f3deae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@ __pycache__/
# Unit test / coverage reports
htmlcov/
+.benchmarks/
.tox/
.coverage
.cache
diff --git a/.travis.yml b/.travis.yml
index 6dcef9e..5663211 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,21 +3,37 @@ language: python
python:
- "2.6"
- "2.7"
- - "3.2"
- "3.3"
- "3.4"
- "3.5"
- "pypy"
- "pypy3"
+matrix:
+ include:
+ - python: "2.7"
+ env: TOXENV=perf
-install:
- - pip install -e .
-script:
- - py.test
+before_install: |
+ pip install tox virtualenv
+ if [ ! -v TOXENV ]
+ then
+ __py_ver=$TRAVIS_PYTHON_VERSION
+ __tox_dfl=${__py_ver/[0-9].[0-9]/py${__py_ver/.}}
+ export TOXENV=${TOXENV:-$__tox_dfl}
+ fi
+ function announce()
+ { echo -e "$ANSI_GREEN$@${ANSI_RESET}"; }
+ function tox()
+ { announce "Running tox in TOXENV=$TOXENV"; env tox "$@"; }
+install: tox --notest
+script: tox
+
+before_cache:
+ - rm -rf $HOME/.cache/pip/log
cache:
directories:
- - $HOME/.cache/pip/http
+ - $HOME/.cache/pip
deploy:
provider: pypi
diff --git a/MANIFEST.in b/MANIFEST.in
index bc85cc3..d2767c3 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,5 +1,5 @@
include MANIFEST.in Makefile LICENSE.txt README.rst CHANGES.rst setup.cfg
-include test_pytest_catchlog.py
+graft tests
global-exclude *pyc
prune __pycache__
diff --git a/Makefile b/Makefile
index c664c1b..67d64cd 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@ clean: clean-pyc clean-coverage
-rm -rv build dist *.egg-info
test:
- py.test -v test_pytest_catchlog.py
+ py.test -v tests
test-coverage:
coverage erase
@@ -27,7 +27,7 @@ test-coverage:
coverage xml
audit:
- flake8 pytest_catchlog.py
+ flake8 pytest_catchlog
wheel:
python setup.py bdist_wheel
diff --git a/README.rst b/README.rst
index 994decb..469e1a5 100644
--- a/README.rst
+++ b/README.rst
@@ -30,12 +30,12 @@ stderr.
Running without options::
- py.test test_pytest_catchlog.py
+ py.test
Shows failed tests like so::
----------------------- Captured stdlog call ----------------------
- test_pytest_catchlog.py 26 INFO text going to logger
+ test_reporting.py 26 INFO text going to logger
----------------------- Captured stdout call ----------------------
text going to stdout
----------------------- Captured stderr call ----------------------
@@ -50,7 +50,7 @@ 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
+ --log-date-format="%Y-%m-%d %H:%M:%S"
Shows failed tests like so::
@@ -77,7 +77,13 @@ error). Command line arguments take precedence.
Further it is possible to disable reporting logs on failed tests
completely with::
- py.test --no-print-logs test_pytest_catchlog.py
+ py.test --no-print-logs
+
+Or in you ``pytest.ini``::
+
+ [pytest]
+ log_print=False
+
Shows failed tests in the normal manner as no logs were captured::
@@ -88,7 +94,7 @@ Shows failed tests in the normal manner as no logs were captured::
==================== 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::
+log messages. This is supported by the ``caplog`` fixture::
def test_foo(caplog):
caplog.set_level(logging.INFO)
@@ -117,7 +123,7 @@ of any logger can be changed instead with::
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
+available on the fixture 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::
@@ -141,3 +147,54 @@ given severity and message::
('root', logging.INFO, 'boo arg'),
]
+You can call ``caplog.clear()`` to reset the captured log records in a test::
+
+ def test_something_with_clearing_records(caplog):
+ some_method_that_creates_log_records()
+ caplog.clear()
+ your_test_method()
+ assert ['Foo'] == [rec.message for rec in caplog.records]
+
+Live Logs
+~~~~~~~~~
+
+By default, catchlog will output any logging records with a level higher or equal
+to WARNING. In order to actually see these logs in the console you have to disable
+pytest output capture by passing ``-s``.
+
+You can specify the logging level for which log records with equal or higher level
+are printed to the console by passing ``--log-cli-level``. This setting accepts the
+logging level names as seen in python's documentation or an integer as the logging
+level num.
+
+Additionally, you can also specify ``--log-cli-format`` and ``--log-cli-date-format``
+which mirror and default to ``--log-format`` and ``--log-date-format`` if not
+provided, but are applied only to the console logging handler.
+
+All of the CLI log options can also be set in the configuration INI file. The option
+names are:
+
+* ``log_cli_level``
+* ``log_cli_format``
+* ``log_cli_date_format``
+
+If you need to record the whole test suite logging calls to a file, you can
+pass
+``--log-file=/path/to/log/file``. This log file is opened in write mode which means
+that it will be overwritten at each run tests session.
+
+You can also specify the logging level for the log file by passing
+``--log-file-level``. This setting accepts the logging level names as seen in python's
+documentation(ie, uppercased level names) or an integer as the logging level num.
+
+Additionally, you can also specify ``--log-file-format`` and ``--log-file-date-format``
+which are equal to ``--log-format`` and ``--log-date-format`` but are applied to the
+log file logging handler.
+
+All of the log file options can also be set in the configuration INI file. The option
+names are:
+
+* ``log_file``
+* ``log_file_level``
+* ``log_file_format``
+* ``log_file_date_format``
diff --git a/pytest_catchlog.py b/pytest_catchlog.py
deleted file mode 100644
index bd86e8b..0000000
--- a/pytest_catchlog.py
+++ /dev/null
@@ -1,313 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, division, print_function
-
-import functools
-import logging
-from contextlib import closing, contextmanager
-
-import pytest
-import py
-
-
-__version__ = '1.2.2'
-
-
-def get_logger_obj(logger=None):
- """Get a logger object that can be specified by its name, or passed as is.
-
- Defaults to the root logger.
- """
- if logger is None or isinstance(logger, py.builtin._basestring):
- logger = logging.getLogger(logger)
- return logger
-
-
- at contextmanager
-def logging_at_level(level, logger=None):
- """Context manager that sets the level for capturing of logs."""
- logger = get_logger_obj(logger)
-
- orig_level = logger.level
- logger.setLevel(level)
- try:
- yield
- finally:
- logger.setLevel(orig_level)
-
-
- at contextmanager
-def logging_using_handler(handler, logger=None):
- """Context manager that safely register a given handler."""
- logger = get_logger_obj(logger)
-
- if handler in logger.handlers: # reentrancy
- # Adding the same handler twice would confuse logging system.
- # Just don't do that.
- yield
- else:
- logger.addHandler(handler)
- try:
- yield
- finally:
- logger.removeHandler(handler)
-
-
- at contextmanager
-def catching_logs(handler, filter=None, formatter=None,
- level=logging.NOTSET, logger=None):
- """Context manager that prepares the whole logging machinery properly."""
- logger = get_logger_obj(logger)
-
- if filter is not None:
- handler.addFilter(filter)
- if formatter is not None:
- handler.setFormatter(formatter)
- handler.setLevel(level)
-
- with closing(handler):
- with logging_using_handler(handler, logger):
- with logging_at_level(min(handler.level, logger.level), logger):
-
- yield handler
-
-
-def add_option_ini(parser, option, dest, default=None, help=None):
- parser.addini(dest, default=default,
- help='default value for ' + option)
- parser.getgroup('catchlog').addoption(option, dest=dest, help=help)
-
-def get_option_ini(config, name):
- ret = config.getoption(name) # 'default' arg won't work as expected
- if ret is None:
- ret = config.getini(name)
- return ret
-
-
-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.')
- add_option_ini(parser,
- '--log-format',
- dest='log_format', default=('%(filename)-25s %(lineno)4d'
- ' %(levelname)-8s %(message)s'),
- help='log format as used by the logging module.')
- add_option_ini(parser,
- '--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.getoption('log_print')
- self.formatter = logging.Formatter(
- get_option_ini(config, 'log_format'),
- get_option_ini(config, 'log_date_format'))
-
- @contextmanager
- def _runtest_for(self, item, when):
- """Implements the internals of pytest_runtest_xxx() hook."""
- with catching_logs(LogCaptureHandler(),
- formatter=self.formatter) as log_handler:
- item.catch_log_handler = log_handler
- try:
- yield # run test
- finally:
- del item.catch_log_handler
-
- if self.print_logs:
- # Add a captured log section to the report.
- log = log_handler.stream.getvalue().strip()
- item.add_report_section(when, 'log', log)
-
- @pytest.mark.hookwrapper
- def pytest_runtest_setup(self, item):
- with self._runtest_for(item, 'setup'):
- yield
-
- @pytest.mark.hookwrapper
- def pytest_runtest_call(self, item):
- with self._runtest_for(item, 'call'):
- yield
-
- @pytest.mark.hookwrapper
- def pytest_runtest_teardown(self, item):
- with self._runtest_for(item, 'teardown'):
- yield
-
-
-class LogCaptureHandler(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 LogCaptureFixture(object):
- """Provides access and control of log capturing."""
-
- @property
- def handler(self):
- return self._item.catch_log_handler
-
- def __init__(self, item):
- """Creates a new funcarg."""
- self._item = item
-
- @property
- def text(self):
- """Returns the log text."""
- return self.handler.stream.getvalue()
-
- @property
- def records(self):
- """Returns the list of log records."""
- return self.handler.records
-
- @property
- 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 logging_at_level(level, obj)
-
-
-class CallablePropertyMixin(object):
- """Backward compatibility for functions that became properties."""
-
- @classmethod
- def compat_property(cls, func):
- if isinstance(func, property):
- make_property = func.getter
- func = func.fget
- else:
- make_property = property
-
- @functools.wraps(func)
- def getter(self):
- naked_value = func(self)
- ret = cls(naked_value)
- ret._naked_value = naked_value
- ret._warn_compat = self._warn_compat
- ret._prop_name = func.__name__
- return ret
-
- return make_property(getter)
-
- def __call__(self):
- self._warn_compat(old="'caplog.{0}()' syntax".format(self._prop_name),
- new="'caplog.{0}' property".format(self._prop_name))
- return self._naked_value # to let legacy clients modify the object
-
-class CallableList(CallablePropertyMixin, list):
- pass
-
-class CallableStr(CallablePropertyMixin, py.builtin.text):
- pass
-
-
-class CompatLogCaptureFixture(LogCaptureFixture):
- """Backward compatibility with pytest-capturelog."""
-
- def _warn_compat(self, old, new):
- self._item.warn(code='L1',
- message=("{0} is deprecated, use {1} instead"
- .format(old, new)))
-
- @CallableStr.compat_property
- def text(self):
- return super(CompatLogCaptureFixture, self).text
-
- @CallableList.compat_property
- def records(self):
- return super(CompatLogCaptureFixture, self).records
-
- @CallableList.compat_property
- def record_tuples(self):
- return super(CompatLogCaptureFixture, self).record_tuples
-
- def setLevel(self, level, logger=None):
- self._warn_compat(old="'caplog.setLevel()'",
- new="'caplog.set_level()'")
- return self.set_level(level, logger)
-
- def atLevel(self, level, logger=None):
- self._warn_compat(old="'caplog.atLevel()'",
- new="'caplog.at_level()'")
- return self.at_level(level, logger)
-
-
- at pytest.fixture
-def caplog(request):
- """Access and control log capturing.
-
- Captured logs are available through the following methods::
-
- * caplog.text() -> string containing formatted log output
- * caplog.records() -> list of logging.LogRecord instances
- * caplog.record_tuples() -> list of (logger_name, level, message) tuples
- """
- return CompatLogCaptureFixture(request.node)
-
-capturelog = caplog
diff --git a/pytest_catchlog/__init__.py b/pytest_catchlog/__init__.py
new file mode 100644
index 0000000..495a173
--- /dev/null
+++ b/pytest_catchlog/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+__version__ = '1.2.2'
diff --git a/pytest_catchlog/common.py b/pytest_catchlog/common.py
new file mode 100644
index 0000000..e5fbf23
--- /dev/null
+++ b/pytest_catchlog/common.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function
+
+import logging
+from contextlib import closing, contextmanager
+
+import py
+
+
+def get_logger_obj(logger=None):
+ """Get a logger object that can be specified by its name, or passed as is.
+
+ Defaults to the root logger.
+ """
+ if logger is None or isinstance(logger, py.builtin._basestring):
+ logger = logging.getLogger(logger)
+ return logger
+
+
+ at contextmanager
+def logging_at_level(level, logger=None):
+ """Context manager that sets the level for capturing of logs."""
+ logger = get_logger_obj(logger)
+
+ orig_level = logger.level
+ logger.setLevel(level)
+ try:
+ yield
+ finally:
+ logger.setLevel(orig_level)
+
+
+ at contextmanager
+def logging_using_handler(handler, logger=None):
+ """Context manager that safely registers a given handler."""
+ logger = get_logger_obj(logger)
+
+ if handler in logger.handlers: # reentrancy
+ # Adding the same handler twice would confuse logging system.
+ # Just don't do that.
+ yield
+ else:
+ logger.addHandler(handler)
+ try:
+ yield
+ finally:
+ logger.removeHandler(handler)
+
+
+ at contextmanager
+def catching_logs(handler, filter=None, formatter=None,
+ level=logging.NOTSET, logger=None):
+ """Context manager that prepares the whole logging machinery properly."""
+ logger = get_logger_obj(logger)
+
+ if filter is not None:
+ handler.addFilter(filter)
+ if formatter is not None:
+ handler.setFormatter(formatter)
+ handler.setLevel(level)
+
+ with closing(handler):
+ with logging_using_handler(handler, logger):
+ with logging_at_level(min(handler.level, logger.level), logger):
+
+ yield handler
diff --git a/pytest_catchlog/fixture.py b/pytest_catchlog/fixture.py
new file mode 100644
index 0000000..ed54bfa
--- /dev/null
+++ b/pytest_catchlog/fixture.py
@@ -0,0 +1,154 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function
+
+import functools
+import logging
+
+import pytest
+import py
+
+from pytest_catchlog.common import catching_logs, logging_at_level
+
+
+class LogCaptureFixture(object):
+ """Provides access and control of log capturing."""
+
+ def __init__(self, item):
+ """Creates a new funcarg."""
+ self._item = item
+
+ @property
+ def handler(self):
+ return self._item.catch_log_handler
+
+ @property
+ def text(self):
+ """Returns the log text."""
+ return self.handler.stream.getvalue()
+
+ @property
+ def records(self):
+ """Returns the list of log records."""
+ return self.handler.records
+
+ @property
+ 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 clear(self):
+ """Reset the list of log records."""
+ self.handler.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 logging_at_level(level, obj)
+
+
+class CallablePropertyMixin(object):
+ """Backward compatibility for functions that became properties."""
+
+ @classmethod
+ def compat_property(cls, func):
+ if isinstance(func, property):
+ make_property = func.getter
+ func = func.fget
+ else:
+ make_property = property
+
+ @functools.wraps(func)
+ def getter(self):
+ naked_value = func(self)
+ ret = cls(naked_value)
+ ret._naked_value = naked_value
+ ret._warn_compat = self._warn_compat
+ ret._prop_name = func.__name__
+ return ret
+
+ return make_property(getter)
+
+ def __call__(self):
+ new = "'caplog.{0}' property".format(self._prop_name)
+ if self._prop_name == 'records':
+ new += ' (or caplog.clear())'
+ self._warn_compat(old="'caplog.{0}()' syntax".format(self._prop_name),
+ new=new)
+ return self._naked_value # to let legacy clients modify the object
+
+
+class CallableList(CallablePropertyMixin, list):
+ pass
+
+
+class CallableStr(CallablePropertyMixin, py.builtin.text):
+ pass
+
+
+class CompatLogCaptureFixture(LogCaptureFixture):
+ """Backward compatibility with pytest-capturelog."""
+
+ def _warn_compat(self, old, new):
+ self._item.warn(code='L1',
+ message=("{0} is deprecated, use {1} instead"
+ .format(old, new)))
+
+ @CallableStr.compat_property
+ def text(self):
+ return super(CompatLogCaptureFixture, self).text
+
+ @CallableList.compat_property
+ def records(self):
+ return super(CompatLogCaptureFixture, self).records
+
+ @CallableList.compat_property
+ def record_tuples(self):
+ return super(CompatLogCaptureFixture, self).record_tuples
+
+ def setLevel(self, level, logger=None):
+ self._warn_compat(old="'caplog.setLevel()'",
+ new="'caplog.set_level()'")
+ return self.set_level(level, logger)
+
+ def atLevel(self, level, logger=None):
+ self._warn_compat(old="'caplog.atLevel()'",
+ new="'caplog.at_level()'")
+ return self.at_level(level, logger)
+
+
+ at pytest.fixture
+def caplog(request):
+ """Access and control log capturing.
+
+ Captured logs are available through the following methods::
+
+ * caplog.text() -> string containing formatted log output
+ * caplog.records() -> list of logging.LogRecord instances
+ * caplog.record_tuples() -> list of (logger_name, level, message) tuples
+ """
+ return CompatLogCaptureFixture(request.node)
+
+capturelog = caplog
diff --git a/pytest_catchlog/plugin.py b/pytest_catchlog/plugin.py
new file mode 100644
index 0000000..fe1bd1d
--- /dev/null
+++ b/pytest_catchlog/plugin.py
@@ -0,0 +1,264 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function
+
+import logging
+import sys
+from contextlib import closing, contextmanager
+
+import pytest
+import py
+
+from pytest_catchlog.common import catching_logs
+
+# Let the fixtures be discoverable by pytest.
+from pytest_catchlog.fixture import caplog, capturelog
+
+
+DEFAULT_LOG_FORMAT = '%(filename)-25s %(lineno)4d %(levelname)-8s %(message)s'
+DEFAULT_LOG_DATE_FORMAT = '%H:%M:%S'
+
+
+def add_option_ini(parser, option, dest, default=None, **kwargs):
+ parser.addini(dest, default=default,
+ help='default value for ' + option)
+ parser.getgroup('catchlog').addoption(option, dest=dest, **kwargs)
+
+
+def get_option_ini(config, name):
+ ret = config.getoption(name) # 'default' arg won't work as expected
+ if ret is None:
+ ret = config.getini(name)
+ return ret
+
+
+def pytest_addoption(parser):
+ """Add options to control log capturing."""
+
+ group = parser.getgroup('catchlog', 'Log catching')
+ add_option_ini(parser,
+ '--no-print-logs',
+ dest='log_print', action='store_const', const=False, default=True,
+ help='disable printing caught logs on failed tests.'
+ )
+ add_option_ini(
+ parser,
+ '--log-level',
+ dest='log_level', default=None,
+ help='logging level used by the logging module'
+ )
+ add_option_ini(parser,
+ '--log-format',
+ dest='log_format', default=DEFAULT_LOG_FORMAT,
+ help='log format as used by the logging module.'
+ )
+ add_option_ini(parser,
+ '--log-date-format',
+ dest='log_date_format', default=DEFAULT_LOG_DATE_FORMAT,
+ help='log date format as used by the logging module.'
+ )
+ add_option_ini(
+ parser,
+ '--log-cli-level',
+ dest='log_cli_level', default=None,
+ help='cli logging level.'
+ )
+ add_option_ini(
+ parser,
+ '--log-cli-format',
+ dest='log_cli_format', default=None,
+ help='log format as used by the logging module.'
+ )
+ add_option_ini(
+ parser,
+ '--log-cli-date-format',
+ dest='log_cli_date_format', default=None,
+ help='log date format as used by the logging module.'
+ )
+ add_option_ini(
+ parser,
+ '--log-file',
+ dest='log_file', default=None,
+ help='path to a file when logging will be written to.'
+ )
+ add_option_ini(
+ parser,
+ '--log-file-level',
+ dest='log_file_level', default=None,
+ help='log file logging level.'
+ )
+ add_option_ini(
+ parser,
+ '--log-file-format',
+ dest='log_file_format', default=DEFAULT_LOG_FORMAT,
+ help='log format as used by the logging module.'
+ )
+ add_option_ini(
+ parser,
+ '--log-file-date-format',
+ dest='log_file_date_format', default=DEFAULT_LOG_DATE_FORMAT,
+ help='log date format as used by the logging module.'
+ )
+
+
+
+def get_actual_log_level(config, setting_name):
+ """Return the actual logging level."""
+ log_level = get_option_ini(config, setting_name)
+ if not log_level:
+ return
+ if isinstance(log_level, py.builtin.text):
+ log_level = log_level.upper()
+ try:
+ return int(getattr(logging, log_level, log_level))
+ except ValueError:
+ # Python logging does not recognise this as a logging level
+ raise pytest.UsageError(
+ "'{0}' is not recognized as a logging level name for "
+ "'{1}'. Please consider passing the "
+ "logging level num instead.".format(
+ log_level,
+ setting_name))
+
+
+def pytest_configure(config):
+ """Always register the log catcher plugin with py.test or tests can't
+ find the fixture function.
+ """
+ log_cli_level = get_actual_log_level(config, 'log_cli_level')
+ if log_cli_level is None:
+ # No specific CLI logging level was provided, let's check
+ # log_level for a fallback
+ log_cli_level = get_actual_log_level(config, 'log_level')
+ if log_cli_level is None:
+ # No log_level was provided, default to WARNING
+ log_cli_level = logging.WARNING
+ config._catchlog_log_cli_level = log_cli_level
+ config._catchlog_log_file = get_option_ini(config, 'log_file')
+ if config._catchlog_log_file:
+ log_file_level = get_actual_log_level(config, 'log_file_level')
+ if log_file_level is None:
+ # No log_level was provided, default to WARNING
+ log_file_level = logging.WARNING
+ config._catchlog_log_file_level = log_file_level
+ 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.
+ """
+ print_logs = get_option_ini(config, 'log_print')
+ if not isinstance(print_logs, bool):
+ if print_logs.lower() in ('true', 'yes', '1'):
+ print_logs = True
+ elif print_logs.lower() in ('false', 'no', '0'):
+ print_logs = False
+ self.print_logs = print_logs
+ self.formatter = logging.Formatter(
+ get_option_ini(config, 'log_format'),
+ get_option_ini(config, 'log_date_format'))
+ self.log_cli_handler = logging.StreamHandler(sys.stderr)
+ log_cli_format = get_option_ini(config, 'log_cli_format')
+ if not log_cli_format:
+ # No CLI specific format was provided, use log_format
+ log_cli_format = get_option_ini(config, 'log_format')
+ log_cli_date_format = get_option_ini(config, 'log_cli_date_format')
+ if not log_cli_date_format:
+ # No CLI specific date format was provided, use log_date_format
+ log_cli_date_format = get_option_ini(config, 'log_date_format')
+ log_cli_formatter = logging.Formatter(
+ log_cli_format,
+ datefmt=log_cli_date_format)
+ self.log_cli_handler.setFormatter(log_cli_formatter)
+ if config._catchlog_log_file:
+ log_file_format = get_option_ini(config, 'log_file_format')
+ if not log_file_format:
+ # No log file specific format was provided, use log_format
+ log_file_format = get_option_ini(config, 'log_format')
+ log_file_date_format = get_option_ini(config, 'log_file_date_format')
+ if not log_file_date_format:
+ # No log file specific date format was provided, use log_date_format
+ log_file_date_format = get_option_ini(config, 'log_date_format')
+ self.log_file_handler = logging.FileHandler(
+ config._catchlog_log_file,
+ # Each pytest runtests session will write to a clean logfile
+ mode='w',
... 1722 lines suppressed ...
--
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