[py-svn] r13417 - in py/dist/py: . log log/testing misc misc/testing
hpk at codespeak.net
hpk at codespeak.net
Wed Jun 15 00:17:18 CEST 2005
Author: hpk
Date: Wed Jun 15 00:17:16 2005
New Revision: 13417
Added:
py/dist/py/log/ (props changed)
py/dist/py/log/__init__.py (contents, props changed)
py/dist/py/log/consumer.py (props changed)
- copied unchanged from r13416, py/dist/py/misc/log_support.py
py/dist/py/log/producer.py (props changed)
- copied unchanged from r13416, py/dist/py/misc/log.py
py/dist/py/log/testing/ (props changed)
py/dist/py/log/testing/__init__.py (contents, props changed)
py/dist/py/log/testing/test_log.py (props changed)
- copied unchanged from r13416, py/dist/py/misc/testing/test_log.py
Removed:
py/dist/py/misc/log.py
py/dist/py/misc/log_support.py
py/dist/py/misc/testing/test_log.py
Modified:
py/dist/py/__init__.py
Log:
move py.log stuff to its own directory
Modified: py/dist/py/__init__.py
==============================================================================
--- py/dist/py/__init__.py (original)
+++ py/dist/py/__init__.py Wed Jun 15 00:17:16 2005
@@ -100,19 +100,21 @@
'xml.escape' : ('./xmlobj/misc.py', 'escape'),
# logging API ('producers' and 'consumers')
- 'log.Producer' : ('./misc/log.py', 'LogProducer'),
- 'log.debug' : ('./misc/log.py', 'debug'),
- 'log.info' : ('./misc/log.py', 'info'),
- 'log.warn' : ('./misc/log.py', 'warn'),
- 'log.error' : ('./misc/log.py', 'error'),
- 'log.critical' : ('./misc/log.py', 'critical'),
- 'log.set_logger' : ('./misc/log.py', 'set_logger'),
- 'log.getstate' : ('./misc/log.py', 'getstate'),
- 'log.setstate' : ('./misc/log.py', 'setstate'),
- 'log.File' : ('./misc/log_support.py', 'File'),
- 'log.Stdout' : ('./misc/log_support.py', 'Stdout'),
- 'log.Stderr' : ('./misc/log_support.py', 'Stderr'),
- 'log.Email' : ('./misc/log_support.py', 'Email'),
- 'log.Syslog' : ('./misc/log_support.py', 'Syslog'),
- 'log.WinEvent' : ('./misc/log_support.py', 'WinEvent'),
+ 'log.Producer' : ('./log/producer.py', 'LogProducer'),
+ 'log.debug' : ('./log/producer.py', 'debug'),
+ 'log.info' : ('./log/producer.py', 'info'),
+ 'log.warn' : ('./log/producer.py', 'warn'),
+ 'log.error' : ('./log/producer.py', 'error'),
+ 'log.critical' : ('./log/producer.py', 'critical'),
+
+ 'log.set_logger' : ('./log/producer.py', 'set_logger'),
+ 'log.getstate' : ('./log/producer.py', 'getstate'),
+ 'log.setstate' : ('./log/producer.py', 'setstate'),
+
+ 'log.File' : ('./log/consumer.py', 'File'),
+ 'log.Stdout' : ('./log/consumer.py', 'Stdout'),
+ 'log.Stderr' : ('./log/consumer.py', 'Stderr'),
+ 'log.Email' : ('./log/consumer.py', 'Email'),
+ 'log.Syslog' : ('./log/consumer.py', 'Syslog'),
+ 'log.WinEvent' : ('./log/consumer.py', 'WinEvent'),
})
Added: py/dist/py/log/__init__.py
==============================================================================
--- (empty file)
+++ py/dist/py/log/__init__.py Wed Jun 15 00:17:16 2005
@@ -0,0 +1 @@
+#
Added: py/dist/py/log/testing/__init__.py
==============================================================================
--- (empty file)
+++ py/dist/py/log/testing/__init__.py Wed Jun 15 00:17:16 2005
@@ -0,0 +1 @@
+#
Deleted: /py/dist/py/misc/log.py
==============================================================================
--- /py/dist/py/misc/log.py Wed Jun 15 00:17:16 2005
+++ (empty file)
@@ -1,85 +0,0 @@
-"""
-py lib's basic logging/tracing functionality
-
- EXPERIMENTAL EXPERIMENTAL EXPERIMENTAL (especially the dispatching)
-
-WARNING: this module is not allowed to contain any 'py' imports,
- Instead, it is very self-contained and should not depend on
- CPython/stdlib versions, either. One reason for these
- restrictions is that this module should be sendable
- via py.execnet across the network in an very early phase.
-"""
-
-class Message(object):
- def __init__(self, keywords, args):
- self.keywords = keywords
- self.args = args
-
- def content(self):
- return " ".join(map(str, self.args))
-
- def prefix(self):
- return "[%s] " % (":".join(self.keywords))
-
- def __str__(self):
- return self.prefix() + self.content()
-
-class LogProducer(object):
- """Log "producer" API which sends messages to be logged
- to a 'consumer' object, which then prints them to stdout,
- stderr, files, etc."""
-
- Message = Message # to allow later customization
- _registry = {}
-
- def __init__(self, keywords):
- if isinstance(keywords, str):
- keywords = tuple(keywords.split())
- self.keywords = keywords
-
- def __repr__(self):
- return "<py.__.LogProducer %s>" % ":".join(self.keywords)
-
- def __getattr__(self, name):
- if name[0] == '_':
- raise AttributeError, name
- return LogProducer(self.keywords + (name,))
-
- def __call__(self, *args):
- message = self.Message(self.keywords, args)
- try:
- func = self._registry[message.keywords]
- except KeyError:
- # XXX find best match, for now it's a hack/simplistic
- try:
- func = self._registry[("default",)]
- except KeyError:
- print str(message)
- return
- func(message)
-
-
-def set_logger(name, func):
- assert callable(func)
- keywords = tuple(map(None, name.split()))
- LogProducer._registry[keywords] = func
- # if default logger is set, also reset the other ones
- # XXX is this a good idea?
- if keywords == ('default',):
- for k in [('debug',), ('info',), ('warn',),
- ('error',), ('critical',)]:
- LogProducer._registry[k] = func
-
-def getstate():
- """ return logging registry state. """
- # class methods dealing with registry
- return LogProducer._registry.copy()
-
-def setstate(state):
- """ set logging registry state. """
- LogProducer._registry = state
-
-# some default severity producers
-_ = globals()
-for x in 'debug info warn split error critical'.split():
- _[x] = LogProducer(x)
Deleted: /py/dist/py/misc/log_support.py
==============================================================================
--- /py/dist/py/misc/log_support.py Wed Jun 15 00:17:16 2005
+++ (empty file)
@@ -1,104 +0,0 @@
-import py
-import os, sys, logging
-import logging.handlers
-
-class LogConsumer(object):
- """Log "consumer" API which receives messages from
- a 'producer' object and displays them using various
- logging mechanisms (stdout, stderr, files, syslog, etc.)"""
-
- _handlers = {}
-
- def __init__(self):
- self.formatter = logging.Formatter('%(message)s')
-
- def File(self, filename, mode='a'):
- filename = str(filename)
- logger_name = "py.log.file.%s" % filename
- handler_type = logging.FileHandler
- handler_args = {'filename': filename,
- 'mode': mode,
- }
- return self._logger_func(logger_name, handler_type, **handler_args)
-
- def Stdout(self):
- # Add str(sys.stdout) to logger name because sys.stdout might be redirected
- # to a file, and in this case we need to distinguish between files
- logger_name = 'py.log.stdout.%s' % str(sys.stdout)
- handler_type = logging.StreamHandler
- handler_args = {'strm': sys.stdout,
- }
- return self._logger_func(logger_name, handler_type, **handler_args)
-
- def Stderr(self):
- # Add str(sys.stderr) to logger name because sys.stderr might be redirected
- # to a file, and in this case we need to distinguish between files
- logger_name = 'py.log.stderr.%s' % str(sys.stderr)
- handler_type = logging.StreamHandler
- handler_args = {'strm': sys.stderr,
- }
- return self._logger_func(logger_name, handler_type, **handler_args)
-
- def Syslog(self, address=('localhost', 514), facility=1):
- logger_name = 'py.log.syslog'
- handler_type = logging.handlers.SysLogHandler
- handler_args = {'address': address,
- 'facility': facility,
- }
- return self._logger_func(logger_name, handler_type, **handler_args)
-
- def WinEvent(self, appname='pylib', logtype='Application'):
- logger_name = 'py.log.winevent'
- handler_type = logging.handlers.NTEventLogHandler
- handler_args = {'appname': appname,
- 'logtype': logtype,
- }
- return self._logger_func(logger_name, handler_type, **handler_args)
-
- def Email(self, mailhost, fromaddr, toaddrs, subject):
- logger_name = 'py.log.email'
- handler_type = logging.handlers.SMTPHandler
- handler_args = {'mailhost': mailhost,
- 'fromaddr': fromaddr,
- 'toaddrs': toaddrs,
- 'subject': subject,
- }
- return self._logger_func(logger_name, handler_type, **handler_args)
-
- def _logger_func(self, logger_name, handler_type, **handler_args):
- logger = logging.getLogger(logger_name)
- #print "got logger " + str(logger) + "for name " + logger_name
- logger.setLevel(logging.DEBUG)
-
- # Add handler to logger only if it hasn't been already set for
- # the same logger name
- if not self._handlers.has_key(logger_name):
- #print "adding handler for logger " + logger_name
- handler = handler_type(**handler_args)
- handler.setFormatter(self.formatter)
- logger.addHandler(handler)
- self._handlers[logger_name] = handler
- def message_processing_func(message):
- self.log_message(logger, message)
- return message_processing_func
-
- def log_message(self, logger, message):
- for keyword in message.keywords:
- if keyword.startswith('debug'):
- logger.debug(message)
- if keyword.startswith('info'):
- logger.info(message)
- if keyword.startswith('warn'):
- logger.warn(message)
- if keyword.startswith('err'):
- logger.error(message)
- if keyword.startswith('crit'):
- logger.critical(message)
-
-consumer = LogConsumer()
-File = consumer.File
-Stdout = consumer.Stdout
-Stderr = consumer.Stderr
-Syslog = consumer.Syslog
-WinEvent = consumer.WinEvent
-Email = consumer.Email
Deleted: /py/dist/py/misc/testing/test_log.py
==============================================================================
--- /py/dist/py/misc/testing/test_log.py Wed Jun 15 00:17:16 2005
+++ (empty file)
@@ -1,398 +0,0 @@
-import py
-import sys
-
-def setup_module(mod):
- mod.tempdir = py.test.ensuretemp("py.log-test")
-
-class TestLogProducer:
- def setup_method(self, meth):
- self.state = py.log.getstate()
- def teardown_method(self, meth):
- py.log.setstate(self.state)
-
- def test_producer_repr(self):
- d = py.log.debug
- assert repr(d).find('debug') != -1
-
- def test_produce_one_keyword(self):
- l = []
- py.log.set_logger('debug', l.append)
- py.log.debug("hello world")
- assert len(l) == 1
- msg = l[0]
- assert msg.content().startswith('hello world')
- assert msg.prefix() == '[debug] '
- assert str(msg) == "[debug] hello world"
-
- def test_producer_class(self):
- p = py.log.Producer('x1')
- l = []
- py.log.set_logger('x1', l.append)
- p("hello")
- assert len(l) == 1
- assert len(l[0].keywords) == 1
- assert 'x1' == l[0].keywords[0]
-
- def test_default_logger(self):
- l = []
- py.log.set_logger("default", l.append)
- py.log.debug("hello")
- py.log.warn("world")
- py.log.info("I")
- py.log.error("am")
- py.log.critical("Sam")
- assert len(l) == 5
- msg1, msg2, msg3, msg4, msg5 = l
-
- assert 'debug' in msg1.keywords
- assert 'warn' in msg2.keywords
- assert 'info' in msg3.keywords
- assert 'error' in msg4.keywords
- assert 'critical' in msg5.keywords
-
- assert msg1.content() == 'hello'
- assert msg2.content() == 'world'
- assert msg3.content() == 'I'
- assert msg4.content() == 'am'
- assert msg5.content() == 'Sam'
-
-class TestLogConsumer:
-
- def test_log_stdout(self):
- # We redirect stdout so that we can verify that
- # the log messages have been printed to it
- p = tempdir.join('log_stdout.out')
- redirect = str(p)
- sys.saved = sys.stdout
- sys.stdout = open(redirect, 'w')
-
- # Start of the 'consumer' code
- py.log.set_logger("default", py.log.Stdout())
- py.log.debug("hello world #1")
- py.log.info("hello world #2")
- py.log.warn("hello world #3")
- py.log.error("hello world #4")
- py.log.critical("hello world #5")
- # End of the 'consumer' code
-
- sys.stdout = sys.saved
- lines = open(redirect).readlines()
- assert lines == ['[debug] hello world #1\n', '[info] hello world #2\n',
- '[warn] hello world #3\n', '[error] hello world #4\n',
- '[critical] hello world #5\n']
-
- def test_log_stderr(self):
- # We redirect stderr so that we can verify that
- # the log messages have been printed to it
- p = tempdir.join('log_stderr.out')
- redirect = str(p)
- sys.saved = sys.stderr
- sys.stderr = open(redirect, 'w')
-
- # Start of the 'consumer' code
- py.log.set_logger("default", py.log.Stderr())
- py.log.debug("hello world #1")
- py.log.info("hello world #2")
- py.log.warn("hello world #3")
- py.log.error("hello world #4")
- py.log.critical("hello world #5")
- # End of the 'consumer' code
-
- sys.stderr = sys.saved
- lines = open(redirect).readlines()
- assert lines == ['[debug] hello world #1\n', '[info] hello world #2\n',
- '[warn] hello world #3\n', '[error] hello world #4\n',
- '[critical] hello world #5\n']
-
- def test_log_file(self):
- custom_log = tempdir.join('log.out')
-
- # Start of the 'consumer' code
- py.log.set_logger("default", py.log.File(custom_log))
- py.log.debug("hello world #1")
- py.log.info("hello world #2")
- py.log.warn("hello world #3")
- py.log.error("hello world #4")
- py.log.critical("hello world #5")
- # End of the 'consumer' code
-
- lines = custom_log.readlines()
- assert lines == ['[debug] hello world #1\n', '[info] hello world #2\n',
- '[warn] hello world #3\n', '[error] hello world #4\n',
- '[critical] hello world #5\n']
-
- def test_log_file_append_mode(self):
- logfilefn = tempdir.join('log_append.out')
-
- # The append mode is on by default, so we don't need to specify it for File
- py.log.set_logger("default", py.log.File(logfilefn))
- py.log.debug("hello world #1")
- py.log.info("hello world #2")
- py.log.warn("hello world #3")
- py.log.error("hello world #4")
- py.log.critical("hello world #5")
-
- lines = logfilefn.readlines()
- assert lines == ['[debug] hello world #1\n', '[info] hello world #2\n',
- '[warn] hello world #3\n', '[error] hello world #4\n',
- '[critical] hello world #5\n']
-
- # We log 5 more lines that should be appended to the log
- py.log.set_logger("default", py.log.File(logfilefn))
- py.log.debug("hello world #6")
- py.log.info("hello world #7")
- py.log.warn("hello world #8")
- py.log.error("hello world #9")
- py.log.critical("hello world #10")
-
- lines = logfilefn.readlines()
- assert lines == ['[debug] hello world #1\n', '[info] hello world #2\n',
- '[warn] hello world #3\n', '[error] hello world #4\n',
- '[critical] hello world #5\n',
- '[debug] hello world #6\n', '[info] hello world #7\n',
- '[warn] hello world #8\n', '[error] hello world #9\n',
- '[critical] hello world #10\n']
-
-
- def test_log_file_write_mode(self):
- logfilefn = tempdir.join('log_write.out')
- logfilefn.write("This line should be zapped when we start logging\n")
-
- # We specify mode='w' for the File
- py.log.set_logger("default", py.log.File(logfilefn, mode='w'))
- py.log.debug("hello world #1")
- py.log.info("hello world #2")
- py.log.warn("hello world #3")
- py.log.error("hello world #4")
- py.log.critical("hello world #5")
-
- lines = logfilefn.readlines()
- assert lines == ['[debug] hello world #1\n', '[info] hello world #2\n',
- '[warn] hello world #3\n', '[error] hello world #4\n',
- '[critical] hello world #5\n']
-
-
- def test_keyword_based_log_files(self):
- logfiledebug = tempdir.join('log_debug.out')
- logfileinfo = tempdir.join('log_info.out')
- logfilewarn = tempdir.join('log_warn.out')
- logfileerror = tempdir.join('log_error.out')
- logfilecritical = tempdir.join('log_critical.out')
-
- py.log.set_logger("debug", py.log.File(logfiledebug))
- py.log.set_logger("info", py.log.File(logfileinfo))
- py.log.set_logger("warn", py.log.File(logfilewarn))
- py.log.set_logger("error", py.log.File(logfileerror))
- py.log.set_logger("critical", py.log.File(logfilecritical))
-
- py.log.debug("hello world #1")
- py.log.info("hello world #2")
- py.log.warn("hello world #3")
- py.log.error("hello world #4")
- py.log.critical("hello world #5")
-
- lines = logfiledebug.readlines()
- assert lines == ['[debug] hello world #1\n']
-
- lines = logfileinfo.readlines()
- assert lines == ['[info] hello world #2\n']
-
- lines = logfilewarn.readlines()
- assert lines == ['[warn] hello world #3\n']
-
- lines = logfileerror.readlines()
- assert lines == ['[error] hello world #4\n']
-
- lines = logfilecritical.readlines()
- assert lines == ['[critical] hello world #5\n']
-
- def test_reassign_default_logger(self):
- logfiledefault1 = tempdir.join('default_log1.out')
-
- # We set a file logger as the default logger
- py.log.set_logger("default", py.log.File(logfiledefault1))
- py.log.debug("hello world #1")
- py.log.info("hello world #2")
- py.log.warn("hello world #3")
- py.log.error("hello world #4")
- py.log.critical("hello world #5")
-
- lines = logfiledefault1.readlines()
- assert lines == ['[debug] hello world #1\n', '[info] hello world #2\n',
- '[warn] hello world #3\n', '[error] hello world #4\n',
- '[critical] hello world #5\n']
-
- # We set a different file logger as the default logger and verify
- # that the new one receives messages and the old one does not receive them anymore
- logfiledefault2 = tempdir.join('default_log2.out')
-
- py.log.set_logger("default", py.log.File(logfiledefault2))
- py.log.debug("hello world #6")
- py.log.info("hello world #7")
- py.log.warn("hello world #8")
- py.log.error("hello world #9")
- py.log.critical("hello world #10")
-
- lines = logfiledefault1.readlines()
- assert lines == ['[debug] hello world #1\n', '[info] hello world #2\n',
- '[warn] hello world #3\n', '[error] hello world #4\n',
- '[critical] hello world #5\n']
-
- lines = logfiledefault2.readlines()
- assert lines == ['[debug] hello world #6\n', '[info] hello world #7\n',
- '[warn] hello world #8\n', '[error] hello world #9\n',
- '[critical] hello world #10\n']
-
- # We set stderr as the default logger and verify that messages go to stderr
- # and not to the previous 2 file loggers
- p = tempdir.join('log_stderr_default.out')
- redirect = str(p)
- saved = sys.stderr
- sys.stderr = open(redirect, 'w')
-
- py.log.set_logger("default", py.log.Stderr())
- py.log.debug("hello world #11")
- py.log.info("hello world #12")
- py.log.warn("hello world #13")
- py.log.error("hello world #14")
- py.log.critical("hello world #15")
-
- sys.stderr = saved
- lines = open(redirect).readlines()
- assert lines == ['[debug] hello world #11\n', '[info] hello world #12\n',
- '[warn] hello world #13\n', '[error] hello world #14\n',
- '[critical] hello world #15\n']
-
- lines = logfiledefault1.readlines()
- assert lines == ['[debug] hello world #1\n', '[info] hello world #2\n',
- '[warn] hello world #3\n', '[error] hello world #4\n',
- '[critical] hello world #5\n']
-
- lines = logfiledefault2.readlines()
- assert lines == ['[debug] hello world #6\n', '[info] hello world #7\n',
- '[warn] hello world #8\n', '[error] hello world #9\n',
- '[critical] hello world #10\n']
-
- def test_reassign_debug_logger(self):
- logfiledefault = tempdir.join('default.out')
- logfiledebug1 = tempdir.join('debug_log1.out')
-
- # We set a file logger as the default logger in non-append mode
- py.log.set_logger("default", py.log.File(logfiledefault, mode='w'))
-
- # We set a file logger as the debug logger
- py.log.set_logger("debug", py.log.File(logfiledebug1))
- py.log.debug("hello world #1")
- py.log.info("hello world #2")
- py.log.warn("hello world #3")
- py.log.error("hello world #4")
- py.log.critical("hello world #5")
-
- # The debug message should have gone to the debug file logger
- lines = logfiledebug1.readlines()
- assert lines == ['[debug] hello world #1\n']
-
- # All other messages should have gone to the default file logger
- lines = logfiledefault.readlines()
- assert lines == ['[info] hello world #2\n',
- '[warn] hello world #3\n', '[error] hello world #4\n',
- '[critical] hello world #5\n']
-
- # We set a different file logger as the debug logger and verify
- # that the new one receives messages and the old one does not receive them anymore
- logfiledebug2 = tempdir.join('debug_log2.out')
-
- py.log.set_logger("debug", py.log.File(logfiledebug2))
- py.log.debug("hello world #6")
- py.log.info("hello world #7")
- py.log.warn("hello world #8")
- py.log.error("hello world #9")
- py.log.critical("hello world #10")
-
- # The debug message should have gone to the new debug file logger
- lines = logfiledebug2.readlines()
- assert lines == ['[debug] hello world #6\n']
-
- # All other messages should have gone to the default file logger
- lines = logfiledefault.readlines()
- assert lines == ['[info] hello world #2\n',
- '[warn] hello world #3\n', '[error] hello world #4\n',
- '[critical] hello world #5\n',
- '[info] hello world #7\n',
- '[warn] hello world #8\n', '[error] hello world #9\n',
- '[critical] hello world #10\n']
-
- # The old debug file logger should be unchanged
- lines = logfiledebug1.readlines()
- assert lines == ['[debug] hello world #1\n']
-
- # We set stdout as the debug logger and verify that messages go to stdout
- # and not to the previous 2 file loggers
- p = tempdir.join('log_stdout_debug.out')
- redirect = str(p)
- saved = sys.stdout
- sys.stdout = open(redirect, 'w')
-
- py.log.set_logger("debug", py.log.Stdout())
- py.log.debug("hello world #11")
- py.log.info("hello world #12")
- py.log.warn("hello world #13")
- py.log.error("hello world #14")
- py.log.critical("hello world #15")
-
- sys.stdout = saved
- # The debug message should have gone to stdout
- lines = open(redirect).readlines()
- assert lines == ['[debug] hello world #11\n']
-
- # All other messages should have gone to the default file logger
- lines = logfiledefault.readlines()
- assert lines == ['[info] hello world #2\n',
- '[warn] hello world #3\n', '[error] hello world #4\n',
- '[critical] hello world #5\n',
- '[info] hello world #7\n',
- '[warn] hello world #8\n', '[error] hello world #9\n',
- '[critical] hello world #10\n',
- '[info] hello world #12\n',
- '[warn] hello world #13\n', '[error] hello world #14\n',
- '[critical] hello world #15\n']
-
- # The 2 old debug file logger should be unchanged
- lines = logfiledebug1.readlines()
- assert lines == ['[debug] hello world #1\n']
-
- lines = logfiledebug2.readlines()
- assert lines == ['[debug] hello world #6\n']
-
- # disabled for now; the syslog log file can usually be read only by root
- # I manually inspected /var/log/messages and the entries were there
- def no_test_log_syslog(self):
- py.log.set_logger("default", py.log.Syslog())
- py.log.debug("hello world #1")
- py.log.info("hello world #2")
- py.log.warn("hello world #3")
- py.log.error("hello world #4")
- py.log.critical("hello world #5")
-
- # disabled for now until I figure out how to read entries in the
- # Event Logs on Windows
- # I manually inspected the Application Log and the entries were there
- def no_test_log_winevent(self):
- py.log.set_logger("default", py.log.WinEvent())
- py.log.debug("hello world #1")
- py.log.info("hello world #2")
- py.log.warn("hello world #3")
- py.log.error("hello world #4")
- py.log.critical("hello world #5")
-
- # disabled for now until I figure out how to properly pass the parameters
- def no_test_log_email(self):
- py.log.set_logger("default", py.log.Email(mailhost="gheorghiu.net",
- fromaddr="grig",
- toaddrs="grig",
- subject = "py.log email"))
- py.log.debug("hello world #1")
- py.log.info("hello world #2")
- py.log.warn("hello world #3")
- py.log.error("hello world #4")
- py.log.critical("hello world #5")
More information about the pytest-commit
mailing list