Skip to content

Commit

Permalink
Merge branch 'master' into gc-after-test#131
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Howitz committed May 25, 2022
2 parents 7fb390c + 0cfed46 commit 39707a2
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 8 deletions.
10 changes: 10 additions & 0 deletions CHANGES.rst
Expand Up @@ -13,6 +13,16 @@
and ``[``*rv*``]`` on higher verbosity levels
(`#131 <https://github.com/zopefoundation/zope.testrunner/issues/131`_).

- Allow the filename for the logging configuration to be specified
via the envvar ``ZOPE_TESTRUNNER_LOG_INI``.
If not defined, the configuration continues to be locked for
in file ``log.ini`` of the current working directory.
Remember the logging configuration file in envvar
``ZOPE_TESTRUNNER_LOG_INI`` to allow spawned child processes
to recreate the logging configuration.
For details, see
`#134 <https://github.com/zopefoundation/zope.testrunner/pull/134>`_.


5.4.0 (2021-11-19)
==================
Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Expand Up @@ -12,5 +12,6 @@ recursive-include docs *.txt
recursive-include docs Makefile

recursive-include src *.py
recursive-include src *.ini
include *.md
recursive-include src *.rst
25 changes: 17 additions & 8 deletions src/zope/testrunner/logsupport.py
Expand Up @@ -18,10 +18,11 @@
The module name is not 'logging' because of a name collision with Python's
logging module.
"""

import logging
import logging.config
import os
import os.path
import warnings

import zope.testrunner.feature

Expand All @@ -31,15 +32,23 @@ class Logging(zope.testrunner.feature.Feature):
active = True

def global_setup(self):
# Get the log.ini file from the current directory instead of
# possibly buried in the build directory. TODO: This isn't
# perfect because if log.ini specifies a log file, it'll be
# relative to the build directory. Hmm... logini =
# os.path.abspath("log.ini")

logini = os.path.abspath("log.ini")
# Get the log.ini file either from the envvar
# ``ZOPE_TESTRUNNER_LOG_INI`` or file ``log.ini`` in
# the current working directory.
logini = os.environ.get("ZOPE_TESTRUNNER_LOG_INI")
if logini is not None and not os.path.exists(logini):
warnings.warn(
"ERROR: file specified by envvar ZOPE_TESTRUNNER_LOG_INI` "
"does not exist")
logini = None
if logini is None:
logini = "log.ini"
logini = os.path.abspath(logini) # make absolute
if os.path.exists(logini):
logging.config.fileConfig(logini, disable_existing_loggers=False)
# remember the log configuration in envvar for use
# by child processes
os.environ["ZOPE_TESTRUNNER_LOG_INI"] = logini
else:
# If there's no log.ini, cause the logging package to be
# silent during testing.
Expand Down
21 changes: 21 additions & 0 deletions src/zope/testrunner/tests/logsupport/log.ini
@@ -0,0 +1,21 @@
[loggers]
keys=root

[handlers]
keys=stderr

[formatters]
keys=minimal

[logger_root]
level=DEBUG
handlers=stderr

[handler_stderr]
level=INFO
class=StreamHandler
args=(sys.stderr,)
formatter=minimal

[formatter_minimal]
format=%(message)s
69 changes: 69 additions & 0 deletions src/zope/testrunner/tests/test_logsupport.py
@@ -0,0 +1,69 @@
from logging import DEBUG
from logging import getLogger
from os import chdir
from os import environ
from os import getcwd
from os.path import dirname
from os.path import join
from unittest import TestCase
from warnings import catch_warnings

from ..logsupport import Logging


setup_logging = Logging.global_setup
setup_logging = getattr(setup_logging, "__func__", setup_logging)
evn = "ZOPE_TESTRUNNER_LOG_INI"


class LogsupportTests(TestCase):
def setUp(self):
# save root logger config
logger = getLogger()
self.logconfig = lc = logger.__dict__.copy()
for n in ("filters", "handlers"):
lc[n] = lc[n][:] # copy
del getattr(logger, n)[:] # clear
# save working directory
self.cwd = getcwd()
# save and clear envvar
self.envvar = ev = environ.get(evn, self)
if ev is not self:
del environ[evn]

def tearDown(self):
# restore working directory
chdir(self.cwd)
# restore root logger configuration
logger = getLogger()
logger.__dict__.update(self.logconfig)
# restore envvar
ev = self.envvar
if ev is not self:
environ[evn] = ev
elif evn in environ:
del environ[evn]

def test_via_cwd(self):
chdir(join(dirname(__file__), "logsupport"))
self.check_logging()

def test_via_envvar(self):
lc = join(dirname(__file__), "logsupport", "log.ini")
environ[evn] = lc
self.check_logging()

def test_via_bad_envvar(self):
chdir(join(dirname(__file__), "logsupport"))
environ[evn] = "not_existing"
with catch_warnings(record=True) as w:
self.check_logging()
self.assertEqual(len(w), 1)

def check_logging(self):
setup_logging(None)
logger = getLogger()
self.assertEqual(logger.level, DEBUG)
self.assertEqual(len(logger.handlers), 1)
lc = join(dirname(__file__), "logsupport", "log.ini")
self.assertEqual(environ[evn], lc)

0 comments on commit 39707a2

Please sign in to comment.