consolidate py/log into fewer files, remove one old approach, sketch simplified API
--HG-- branch : trunk
This commit is contained in:
parent
2b8f489d60
commit
27c08ac235
|
@ -1,6 +1,11 @@
|
||||||
Changes between 1.0.x and 'trunk'
|
Changes between 1.0.x and 'trunk'
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
* consolidate py.log implementation, remove old approach.
|
||||||
|
|
||||||
|
* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
|
||||||
|
text/unicode and byte-streams (uses underlying standard lib io.*
|
||||||
|
if available)
|
||||||
* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
|
* introduce py.io.TextIO and py.io.BytesIO for distinguishing between
|
||||||
text/unicode and byte-streams (uses underlying standard lib io.*
|
text/unicode and byte-streams (uses underlying standard lib io.*
|
||||||
if available)
|
if available)
|
||||||
|
|
|
@ -176,17 +176,15 @@ initpkg(__name__,
|
||||||
|
|
||||||
# logging API ('producers' and 'consumers' connected via keywords)
|
# logging API ('producers' and 'consumers' connected via keywords)
|
||||||
'log.__doc__' : ('./log/__init__.py', '__doc__'),
|
'log.__doc__' : ('./log/__init__.py', '__doc__'),
|
||||||
'log._apiwarn' : ('./log/warning.py', '_apiwarn'),
|
'log._apiwarn' : ('./log/warning.py', '_apiwarn'),
|
||||||
'log.Producer' : ('./log/producer.py', 'Producer'),
|
'log.Producer' : ('./log/log.py', 'Producer'),
|
||||||
'log.default' : ('./log/producer.py', 'default'),
|
'log.setconsumer' : ('./log/log.py', 'setconsumer'),
|
||||||
'log._getstate' : ('./log/producer.py', '_getstate'),
|
'log._setstate' : ('./log/log.py', 'setstate'),
|
||||||
'log._setstate' : ('./log/producer.py', '_setstate'),
|
'log._getstate' : ('./log/log.py', 'getstate'),
|
||||||
'log.setconsumer' : ('./log/consumer.py', 'setconsumer'),
|
'log.Path' : ('./log/log.py', 'Path'),
|
||||||
'log.Path' : ('./log/consumer.py', 'Path'),
|
'log.STDOUT' : ('./log/log.py', 'STDOUT'),
|
||||||
'log.STDOUT' : ('./log/consumer.py', 'STDOUT'),
|
'log.STDERR' : ('./log/log.py', 'STDERR'),
|
||||||
'log.STDERR' : ('./log/consumer.py', 'STDERR'),
|
'log.Syslog' : ('./log/log.py', 'Syslog'),
|
||||||
'log.Syslog' : ('./log/consumer.py', 'Syslog'),
|
|
||||||
'log.get' : ('./log/logger.py', 'get'),
|
|
||||||
|
|
||||||
# compatibility modules (taken from 2.4.4)
|
# compatibility modules (taken from 2.4.4)
|
||||||
'compat.__doc__' : ('./compat/__init__.py', '__doc__'),
|
'compat.__doc__' : ('./compat/__init__.py', '__doc__'),
|
||||||
|
|
|
@ -1,79 +0,0 @@
|
||||||
import py
|
|
||||||
import sys
|
|
||||||
|
|
||||||
class File(object):
|
|
||||||
""" log consumer wrapping a file(-like) object
|
|
||||||
"""
|
|
||||||
def __init__(self, f):
|
|
||||||
assert hasattr(f, 'write')
|
|
||||||
assert isinstance(f, file) or not hasattr(f, 'open')
|
|
||||||
self._file = f
|
|
||||||
|
|
||||||
def __call__(self, msg):
|
|
||||||
""" write a message to the log """
|
|
||||||
print >>self._file, str(msg)
|
|
||||||
|
|
||||||
class Path(object):
|
|
||||||
""" log consumer able to write log messages into
|
|
||||||
"""
|
|
||||||
def __init__(self, filename, append=False, delayed_create=False,
|
|
||||||
buffering=1):
|
|
||||||
self._append = append
|
|
||||||
self._filename = filename
|
|
||||||
self._buffering = buffering
|
|
||||||
if not delayed_create:
|
|
||||||
self._openfile()
|
|
||||||
|
|
||||||
def _openfile(self):
|
|
||||||
mode = self._append and 'a' or 'w'
|
|
||||||
f = open(str(self._filename), mode, buffering=self._buffering)
|
|
||||||
self._file = f
|
|
||||||
|
|
||||||
def __call__(self, msg):
|
|
||||||
""" write a message to the log """
|
|
||||||
if not hasattr(self, "_file"):
|
|
||||||
self._openfile()
|
|
||||||
print >> self._file, msg
|
|
||||||
|
|
||||||
def STDOUT(msg):
|
|
||||||
""" consumer that writes to sys.stdout """
|
|
||||||
print >>sys.stdout, str(msg)
|
|
||||||
|
|
||||||
def STDERR(msg):
|
|
||||||
""" consumer that writes to sys.stderr """
|
|
||||||
print >>sys.stderr, str(msg)
|
|
||||||
|
|
||||||
class Syslog:
|
|
||||||
""" consumer that writes to the syslog daemon """
|
|
||||||
|
|
||||||
for priority in "LOG_EMERG LOG_ALERT LOG_CRIT LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_DEBUG".split():
|
|
||||||
try:
|
|
||||||
exec("%s = py.std.syslog.%s" % (priority, priority))
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def __init__(self, priority = None):
|
|
||||||
self.priority = self.LOG_INFO
|
|
||||||
if priority is not None:
|
|
||||||
self.priority = priority
|
|
||||||
|
|
||||||
def __call__(self, msg):
|
|
||||||
""" write a message to the log """
|
|
||||||
py.std.syslog.syslog(self.priority, str(msg))
|
|
||||||
|
|
||||||
|
|
||||||
def setconsumer(keywords, consumer):
|
|
||||||
""" create a consumer for a set of keywords """
|
|
||||||
# normalize to tuples
|
|
||||||
if isinstance(keywords, str):
|
|
||||||
keywords = tuple(map(None, keywords.split()))
|
|
||||||
elif hasattr(keywords, 'keywords'):
|
|
||||||
keywords = keywords.keywords
|
|
||||||
elif not isinstance(keywords, tuple):
|
|
||||||
raise TypeError("key %r is not a string or tuple" % (keywords,))
|
|
||||||
if consumer is not None and not callable(consumer):
|
|
||||||
if not hasattr(consumer, 'write'):
|
|
||||||
raise TypeError("%r should be None, callable or file-like" % (consumer,))
|
|
||||||
consumer = File(consumer)
|
|
||||||
py.log.Producer(keywords).set_consumer(consumer)
|
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
"""
|
||||||
|
basic logging functionality based on a producer/consumer scheme.
|
||||||
|
|
||||||
|
XXX implement this API: (maybe put it into slogger.py?)
|
||||||
|
|
||||||
|
log = Logger(
|
||||||
|
info=py.log.STDOUT,
|
||||||
|
debug=py.log.STDOUT,
|
||||||
|
command=None)
|
||||||
|
log.info("hello", "world")
|
||||||
|
log.command("hello", "world")
|
||||||
|
|
||||||
|
log = Logger(info=Logger(something=...),
|
||||||
|
debug=py.log.STDOUT,
|
||||||
|
command=None)
|
||||||
|
"""
|
||||||
|
import py, sys
|
||||||
|
|
||||||
|
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 Producer(object):
|
||||||
|
""" (deprecated) Log producer API which sends messages to be logged
|
||||||
|
to a 'consumer' object, which then prints them to stdout,
|
||||||
|
stderr, files, etc. Used extensively by PyPy-1.1.
|
||||||
|
"""
|
||||||
|
|
||||||
|
Message = Message # to allow later customization
|
||||||
|
keywords2consumer = {}
|
||||||
|
|
||||||
|
def __init__(self, keywords, keywordmapper=None, **kw):
|
||||||
|
if hasattr(keywords, 'split'):
|
||||||
|
keywords = tuple(keywords.split())
|
||||||
|
self._keywords = keywords
|
||||||
|
if keywordmapper is None:
|
||||||
|
keywordmapper = default_keywordmapper
|
||||||
|
self._keywordmapper = keywordmapper
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<py.log.Producer %s>" % ":".join(self._keywords)
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if '_' in name:
|
||||||
|
raise AttributeError(name)
|
||||||
|
producer = self.__class__(self._keywords + (name,))
|
||||||
|
setattr(self, name, producer)
|
||||||
|
return producer
|
||||||
|
|
||||||
|
def __call__(self, *args):
|
||||||
|
""" write a message to the appropriate consumer(s) """
|
||||||
|
func = self._keywordmapper.getconsumer(self._keywords)
|
||||||
|
if func is not None:
|
||||||
|
func(self.Message(self._keywords, args))
|
||||||
|
|
||||||
|
class KeywordMapper:
|
||||||
|
def __init__(self):
|
||||||
|
self.keywords2consumer = {}
|
||||||
|
|
||||||
|
def getstate(self):
|
||||||
|
return self.keywords2consumer.copy()
|
||||||
|
def setstate(self, state):
|
||||||
|
self.keywords2consumer.clear()
|
||||||
|
self.keywords2consumer.update(state)
|
||||||
|
|
||||||
|
def getconsumer(self, keywords):
|
||||||
|
""" return a consumer matching the given keywords.
|
||||||
|
|
||||||
|
tries to find the most suitable consumer by walking, starting from
|
||||||
|
the back, the list of keywords, the first consumer matching a
|
||||||
|
keyword is returned (falling back to py.log.default)
|
||||||
|
"""
|
||||||
|
for i in range(len(keywords), 0, -1):
|
||||||
|
try:
|
||||||
|
return self.keywords2consumer[keywords[:i]]
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
return self.keywords2consumer.get('default', default_consumer)
|
||||||
|
|
||||||
|
def setconsumer(self, keywords, consumer):
|
||||||
|
""" set a consumer for a set of keywords. """
|
||||||
|
# normalize to tuples
|
||||||
|
if isinstance(keywords, str):
|
||||||
|
keywords = tuple(map(None, keywords.split()))
|
||||||
|
elif hasattr(keywords, '_keywords'):
|
||||||
|
keywords = keywords._keywords
|
||||||
|
elif not isinstance(keywords, tuple):
|
||||||
|
raise TypeError("key %r is not a string or tuple" % (keywords,))
|
||||||
|
if consumer is not None and not callable(consumer):
|
||||||
|
if not hasattr(consumer, 'write'):
|
||||||
|
raise TypeError(
|
||||||
|
"%r should be None, callable or file-like" % (consumer,))
|
||||||
|
consumer = File(consumer)
|
||||||
|
self.keywords2consumer[keywords] = consumer
|
||||||
|
|
||||||
|
def default_consumer(msg):
|
||||||
|
""" the default consumer, prints the message to stdout (using 'print') """
|
||||||
|
sys.stderr.write(str(msg)+"\n")
|
||||||
|
|
||||||
|
default_keywordmapper = KeywordMapper()
|
||||||
|
|
||||||
|
def setconsumer(keywords, consumer):
|
||||||
|
default_keywordmapper.setconsumer(keywords, consumer)
|
||||||
|
|
||||||
|
def setstate(state):
|
||||||
|
default_keywordmapper.setstate(state)
|
||||||
|
def getstate():
|
||||||
|
return default_keywordmapper.getstate()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Consumers
|
||||||
|
#
|
||||||
|
|
||||||
|
class File(object):
|
||||||
|
""" log consumer wrapping a file(-like) object
|
||||||
|
"""
|
||||||
|
def __init__(self, f):
|
||||||
|
assert hasattr(f, 'write')
|
||||||
|
assert isinstance(f, file) or not hasattr(f, 'open')
|
||||||
|
self._file = f
|
||||||
|
|
||||||
|
def __call__(self, msg):
|
||||||
|
""" write a message to the log """
|
||||||
|
self._file.write(str(msg) + "\n")
|
||||||
|
|
||||||
|
class Path(object):
|
||||||
|
""" log consumer able to write log messages into
|
||||||
|
"""
|
||||||
|
def __init__(self, filename, append=False, delayed_create=False,
|
||||||
|
buffering=1):
|
||||||
|
self._append = append
|
||||||
|
self._filename = filename
|
||||||
|
self._buffering = buffering
|
||||||
|
if not delayed_create:
|
||||||
|
self._openfile()
|
||||||
|
|
||||||
|
def _openfile(self):
|
||||||
|
mode = self._append and 'a' or 'w'
|
||||||
|
f = open(str(self._filename), mode, buffering=self._buffering)
|
||||||
|
self._file = f
|
||||||
|
|
||||||
|
def __call__(self, msg):
|
||||||
|
""" write a message to the log """
|
||||||
|
if not hasattr(self, "_file"):
|
||||||
|
self._openfile()
|
||||||
|
self._file.write(str(msg) + "\n")
|
||||||
|
|
||||||
|
def STDOUT(msg):
|
||||||
|
""" consumer that writes to sys.stdout """
|
||||||
|
sys.stdout.write(str(msg)+"\n")
|
||||||
|
|
||||||
|
def STDERR(msg):
|
||||||
|
""" consumer that writes to sys.stderr """
|
||||||
|
sys.stderr.write(str(msg)+"\n")
|
||||||
|
|
||||||
|
class Syslog:
|
||||||
|
""" consumer that writes to the syslog daemon """
|
||||||
|
|
||||||
|
for priority in "LOG_EMERG LOG_ALERT LOG_CRIT LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_DEBUG".split():
|
||||||
|
try:
|
||||||
|
exec("%s = py.std.syslog.%s" % (priority, priority))
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __init__(self, priority = None):
|
||||||
|
if priority is None:
|
||||||
|
priority = self.LOG_INFO
|
||||||
|
self.priority = priority
|
||||||
|
|
||||||
|
def __call__(self, msg):
|
||||||
|
""" write a message to the log """
|
||||||
|
py.std.syslog.syslog(self.priority, str(msg))
|
|
@ -1,71 +0,0 @@
|
||||||
|
|
||||||
class Message(object):
|
|
||||||
def __init__(self, processor, *args):
|
|
||||||
self.content = args
|
|
||||||
self.processor = processor
|
|
||||||
self.keywords = (processor.logger._ident,
|
|
||||||
processor.name)
|
|
||||||
|
|
||||||
def strcontent(self):
|
|
||||||
return " ".join(map(str, self.content))
|
|
||||||
|
|
||||||
def strprefix(self):
|
|
||||||
return '[%s] ' % ":".join(map(str, self.keywords))
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.strprefix() + self.strcontent()
|
|
||||||
|
|
||||||
class Processor(object):
|
|
||||||
def __init__(self, logger, name, consume):
|
|
||||||
self.logger = logger
|
|
||||||
self.name = name
|
|
||||||
self.consume = consume
|
|
||||||
|
|
||||||
def __call__(self, *args):
|
|
||||||
try:
|
|
||||||
consume = self.logger._override
|
|
||||||
except AttributeError:
|
|
||||||
consume = self.consume
|
|
||||||
if consume is not None:
|
|
||||||
msg = Message(self, *args)
|
|
||||||
consume(msg)
|
|
||||||
|
|
||||||
class Logger(object):
|
|
||||||
_key2logger = {}
|
|
||||||
|
|
||||||
def __init__(self, ident):
|
|
||||||
self._ident = ident
|
|
||||||
self._key2logger[ident] = self
|
|
||||||
self._keywords = ()
|
|
||||||
|
|
||||||
def set_sub(self, **kwargs):
|
|
||||||
for name, value in kwargs.items():
|
|
||||||
self._setsub(name, value)
|
|
||||||
|
|
||||||
def ensure_sub(self, **kwargs):
|
|
||||||
for name, value in kwargs.items():
|
|
||||||
if not hasattr(self, name):
|
|
||||||
self._setsub(name, value)
|
|
||||||
|
|
||||||
def set_override(self, consumer):
|
|
||||||
self._override = lambda msg: consumer(msg)
|
|
||||||
|
|
||||||
def del_override(self):
|
|
||||||
try:
|
|
||||||
del self._override
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _setsub(self, name, dest):
|
|
||||||
assert "_" not in name
|
|
||||||
setattr(self, name, Processor(self, name, dest))
|
|
||||||
|
|
||||||
def get(ident="global", **kwargs):
|
|
||||||
""" return the Logger with id 'ident', instantiating if appropriate """
|
|
||||||
try:
|
|
||||||
log = Logger._key2logger[ident]
|
|
||||||
except KeyError:
|
|
||||||
log = Logger(ident)
|
|
||||||
log.ensure_sub(**kwargs)
|
|
||||||
return log
|
|
||||||
|
|
|
@ -1,88 +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 Producer(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
|
|
||||||
keywords2consumer = {}
|
|
||||||
|
|
||||||
def __init__(self, keywords):
|
|
||||||
if isinstance(keywords, str):
|
|
||||||
keywords = tuple(keywords.split())
|
|
||||||
self.keywords = keywords
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<py.log.Producer %s>" % ":".join(self.keywords)
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
if '_' in name:
|
|
||||||
raise AttributeError, name
|
|
||||||
producer = self.__class__(self.keywords + (name,))
|
|
||||||
setattr(self, name, producer)
|
|
||||||
return producer
|
|
||||||
|
|
||||||
def __call__(self, *args):
|
|
||||||
""" write a message to the appropriate consumer(s) """
|
|
||||||
func = self.get_consumer(self.keywords)
|
|
||||||
if func is not None:
|
|
||||||
func(self.Message(self.keywords, args))
|
|
||||||
|
|
||||||
def get_consumer(self, keywords):
|
|
||||||
""" return a consumer matching keywords
|
|
||||||
|
|
||||||
tries to find the most suitable consumer by walking, starting from
|
|
||||||
the back, the list of keywords, the first consumer matching a
|
|
||||||
keyword is returned (falling back to py.log.default)
|
|
||||||
"""
|
|
||||||
for i in range(len(self.keywords), 0, -1):
|
|
||||||
try:
|
|
||||||
return self.keywords2consumer[self.keywords[:i]]
|
|
||||||
except KeyError:
|
|
||||||
continue
|
|
||||||
return self.keywords2consumer.get('default', default_consumer)
|
|
||||||
|
|
||||||
def set_consumer(self, consumer):
|
|
||||||
""" register a consumer matching our own keywords """
|
|
||||||
self.keywords2consumer[self.keywords] = consumer
|
|
||||||
|
|
||||||
default = Producer('default')
|
|
||||||
|
|
||||||
def _getstate():
|
|
||||||
return Producer.keywords2consumer.copy()
|
|
||||||
|
|
||||||
def _setstate(state):
|
|
||||||
Producer.keywords2consumer.clear()
|
|
||||||
Producer.keywords2consumer.update(state)
|
|
||||||
|
|
||||||
def default_consumer(msg):
|
|
||||||
""" the default consumer, prints the message to stdout (using 'print') """
|
|
||||||
print str(msg)
|
|
||||||
|
|
||||||
Producer.keywords2consumer['default'] = default_consumer
|
|
|
@ -1,24 +1,32 @@
|
||||||
import py
|
import py
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from py.__.log.log import default_keywordmapper
|
||||||
|
|
||||||
callcapture = py.io.StdCapture.call
|
callcapture = py.io.StdCapture.call
|
||||||
|
|
||||||
def setup_module(mod):
|
def setup_module(mod):
|
||||||
mod.tempdir = py.test.ensuretemp("py.log-test")
|
mod.tempdir = py.test.ensuretemp("py.log-test")
|
||||||
mod.logstate = py.log._getstate()
|
mod._oldstate = default_keywordmapper.getstate()
|
||||||
|
|
||||||
def teardown_module(mod):
|
def teardown_module(mod):
|
||||||
py.log._setstate(mod.logstate)
|
default_keywordmapper.setstate(mod._oldstate)
|
||||||
|
|
||||||
class TestLogProducer:
|
class TestLogProducer:
|
||||||
def setup_method(self, meth):
|
def setup_method(self, meth):
|
||||||
self.state = py.log._getstate()
|
default_keywordmapper.setstate(_oldstate)
|
||||||
|
|
||||||
def teardown_method(self, meth):
|
def test_getstate_setstate(self):
|
||||||
py.log._setstate(self.state)
|
state = py.log._getstate()
|
||||||
|
py.log.setconsumer("hello", [].append)
|
||||||
|
state2 = py.log._getstate()
|
||||||
|
assert state2 != state
|
||||||
|
py.log._setstate(state)
|
||||||
|
state3 = py.log._getstate()
|
||||||
|
assert state3 == state
|
||||||
|
|
||||||
def test_producer_repr(self):
|
def test_producer_repr(self):
|
||||||
d = py.log.default
|
d = py.log.Producer("default")
|
||||||
assert repr(d).find('default') != -1
|
assert repr(d).find('default') != -1
|
||||||
|
|
||||||
def test_produce_one_keyword(self):
|
def test_produce_one_keyword(self):
|
||||||
|
@ -34,7 +42,7 @@ class TestLogProducer:
|
||||||
def test_producer_class(self):
|
def test_producer_class(self):
|
||||||
p = py.log.Producer('x1')
|
p = py.log.Producer('x1')
|
||||||
l = []
|
l = []
|
||||||
py.log.setconsumer(p.keywords, l.append)
|
py.log.setconsumer(p._keywords, l.append)
|
||||||
p("hello")
|
p("hello")
|
||||||
assert len(l) == 1
|
assert len(l) == 1
|
||||||
assert len(l[0].keywords) == 1
|
assert len(l[0].keywords) == 1
|
||||||
|
@ -47,10 +55,7 @@ class TestLogProducer:
|
||||||
|
|
||||||
class TestLogConsumer:
|
class TestLogConsumer:
|
||||||
def setup_method(self, meth):
|
def setup_method(self, meth):
|
||||||
self.state = py.log._getstate()
|
default_keywordmapper.setstate(_oldstate)
|
||||||
def teardown_method(self, meth):
|
|
||||||
py.log._setstate(self.state)
|
|
||||||
|
|
||||||
def test_log_none(self):
|
def test_log_none(self):
|
||||||
log = py.log.Producer("XXX")
|
log = py.log.Producer("XXX")
|
||||||
l = []
|
l = []
|
||||||
|
@ -62,9 +67,9 @@ class TestLogConsumer:
|
||||||
log("2")
|
log("2")
|
||||||
assert not l
|
assert not l
|
||||||
|
|
||||||
def test_log_default_stdout(self):
|
def test_log_default_stderr(self):
|
||||||
res, out, err = callcapture(py.log.default, "hello")
|
res, out, err = callcapture(py.log.Producer("default"), "hello")
|
||||||
assert out.strip() == "[default] hello"
|
assert err.strip() == "[default] hello"
|
||||||
|
|
||||||
def test_simple_consumer_match(self):
|
def test_simple_consumer_match(self):
|
||||||
l = []
|
l = []
|
||||||
|
@ -77,7 +82,7 @@ class TestLogConsumer:
|
||||||
def test_simple_consumer_match_2(self):
|
def test_simple_consumer_match_2(self):
|
||||||
l = []
|
l = []
|
||||||
p = py.log.Producer("x1 x2")
|
p = py.log.Producer("x1 x2")
|
||||||
p.set_consumer(l.append)
|
py.log.setconsumer(p._keywords, l.append)
|
||||||
p("42")
|
p("42")
|
||||||
assert l
|
assert l
|
||||||
assert l[0].content() == "42"
|
assert l[0].content() == "42"
|
||||||
|
@ -106,19 +111,19 @@ class TestLogConsumer:
|
||||||
assert l[0].content() == "hello"
|
assert l[0].content() == "hello"
|
||||||
|
|
||||||
def test_log_stderr(self):
|
def test_log_stderr(self):
|
||||||
py.log.setconsumer("default", py.log.STDERR)
|
py.log.setconsumer("xyz", py.log.STDOUT)
|
||||||
res, out, err = callcapture(py.log.default, "hello")
|
res, out, err = callcapture(py.log.Producer("xyz"), "hello")
|
||||||
assert not out
|
assert not err
|
||||||
assert err.strip() == '[default] hello'
|
assert out.strip() == '[xyz] hello'
|
||||||
|
|
||||||
def test_log_file(self):
|
def test_log_file(self):
|
||||||
custom_log = tempdir.join('log.out')
|
custom_log = tempdir.join('log.out')
|
||||||
py.log.setconsumer("default", open(str(custom_log), 'w', buffering=0))
|
py.log.setconsumer("default", open(str(custom_log), 'w', buffering=0))
|
||||||
py.log.default("hello world #1")
|
py.log.Producer("default")("hello world #1")
|
||||||
assert custom_log.readlines() == ['[default] hello world #1\n']
|
assert custom_log.readlines() == ['[default] hello world #1\n']
|
||||||
|
|
||||||
py.log.setconsumer("default", py.log.Path(custom_log, buffering=0))
|
py.log.setconsumer("default", py.log.Path(custom_log, buffering=0))
|
||||||
py.log.default("hello world #2")
|
py.log.Producer("default")("hello world #2")
|
||||||
assert custom_log.readlines() == ['[default] hello world #2\n'] # no append by default!
|
assert custom_log.readlines() == ['[default] hello world #2\n'] # no append by default!
|
||||||
|
|
||||||
def test_log_file_append_mode(self):
|
def test_log_file_append_mode(self):
|
||||||
|
@ -128,12 +133,12 @@ class TestLogConsumer:
|
||||||
py.log.setconsumer("default", py.log.Path(logfilefn, append=True,
|
py.log.setconsumer("default", py.log.Path(logfilefn, append=True,
|
||||||
buffering=0))
|
buffering=0))
|
||||||
assert logfilefn.check()
|
assert logfilefn.check()
|
||||||
py.log.default("hello world #1")
|
py.log.Producer("default")("hello world #1")
|
||||||
lines = logfilefn.readlines()
|
lines = logfilefn.readlines()
|
||||||
assert lines == ['[default] hello world #1\n']
|
assert lines == ['[default] hello world #1\n']
|
||||||
py.log.setconsumer("default", py.log.Path(logfilefn, append=True,
|
py.log.setconsumer("default", py.log.Path(logfilefn, append=True,
|
||||||
buffering=0))
|
buffering=0))
|
||||||
py.log.default("hello world #1")
|
py.log.Producer("default")("hello world #1")
|
||||||
lines = logfilefn.readlines()
|
lines = logfilefn.readlines()
|
||||||
assert lines == ['[default] hello world #1\n',
|
assert lines == ['[default] hello world #1\n',
|
||||||
'[default] hello world #1\n']
|
'[default] hello world #1\n']
|
||||||
|
@ -144,7 +149,7 @@ class TestLogConsumer:
|
||||||
py.log.setconsumer("default", py.log.Path(logfilefn,
|
py.log.setconsumer("default", py.log.Path(logfilefn,
|
||||||
delayed_create=True, buffering=0))
|
delayed_create=True, buffering=0))
|
||||||
assert not logfilefn.check()
|
assert not logfilefn.check()
|
||||||
py.log.default("hello world #1")
|
py.log.Producer("default")("hello world #1")
|
||||||
lines = logfilefn.readlines()
|
lines = logfilefn.readlines()
|
||||||
assert lines == ['[default] hello world #1\n']
|
assert lines == ['[default] hello world #1\n']
|
||||||
|
|
||||||
|
|
|
@ -1,101 +0,0 @@
|
||||||
|
|
||||||
import py
|
|
||||||
|
|
||||||
def test_logger_identity():
|
|
||||||
assert py.log.get() is py.log.get()
|
|
||||||
otherkey = object()
|
|
||||||
for key in "name1", object():
|
|
||||||
log = py.log.get(key)
|
|
||||||
assert py.log.get(key) is log
|
|
||||||
assert py.log.get(otherkey) is not log
|
|
||||||
|
|
||||||
def test_log_preset():
|
|
||||||
log = py.log.get(test_log_preset)
|
|
||||||
l2 = []
|
|
||||||
log.set_sub(x1=None, x2=l2.append)
|
|
||||||
l3 = []
|
|
||||||
log2 = py.log.get(test_log_preset,
|
|
||||||
x2=None,
|
|
||||||
x3=l3.append)
|
|
||||||
|
|
||||||
log2.x2("hello")
|
|
||||||
log2.x3("world")
|
|
||||||
assert l2[0].strcontent() == "hello"
|
|
||||||
assert l3[0].strcontent() == "world"
|
|
||||||
|
|
||||||
def test_log_override():
|
|
||||||
l2 = []
|
|
||||||
log = py.log.get(object(), x1=None, x2=l2.append)
|
|
||||||
l = []
|
|
||||||
log.set_override(l.append)
|
|
||||||
log.x1("hello")
|
|
||||||
log.x2("world")
|
|
||||||
log.ensure_sub(x3=None)
|
|
||||||
log.x3(42)
|
|
||||||
assert len(l) == 3
|
|
||||||
assert not l2
|
|
||||||
r = [x.strcontent() for x in l]
|
|
||||||
assert r == ["hello", "world", "42"]
|
|
||||||
l[:] = []
|
|
||||||
log.del_override()
|
|
||||||
log.del_override()
|
|
||||||
log.x2("hello")
|
|
||||||
assert l2[0].strcontent() == "hello"
|
|
||||||
|
|
||||||
def test_log_basic():
|
|
||||||
l1 = []
|
|
||||||
class SomeKey:
|
|
||||||
def __str__(self):
|
|
||||||
return "somekey"
|
|
||||||
|
|
||||||
for key in "name1", SomeKey():
|
|
||||||
log = py.log.get(key)
|
|
||||||
log.set_sub(x1=l1.append)
|
|
||||||
log.x1(42)
|
|
||||||
assert l1[-1].content == (42,)
|
|
||||||
assert l1[-1].strcontent() == "42"
|
|
||||||
assert l1[-1].keywords == (key, 'x1')
|
|
||||||
assert l1[-1].strprefix() == "[%s:x1] " %(key,)
|
|
||||||
|
|
||||||
#log.set_prefix("hello")
|
|
||||||
#assert l1[0].strprefix() == "hello"
|
|
||||||
#log("world")
|
|
||||||
#assert str(l1[-1]) == "hello world"
|
|
||||||
|
|
||||||
class TestLogger:
|
|
||||||
def setup_method(self, method):
|
|
||||||
self._x1 = []
|
|
||||||
self._x2 = []
|
|
||||||
self.log = py.log.get()
|
|
||||||
self.log.set_sub(x1=self._x1.append,
|
|
||||||
x2=self._x2.append)
|
|
||||||
|
|
||||||
#def teardown_method(self, method):
|
|
||||||
# self.log.close()
|
|
||||||
|
|
||||||
def test_simple(self):
|
|
||||||
self.log.x1("hello")
|
|
||||||
self.log.x2("world")
|
|
||||||
assert self._x1[0].strcontent() == 'hello'
|
|
||||||
assert self._x1[0].strprefix() == '[global:x1] '
|
|
||||||
assert self._x2[0].strcontent() == 'world'
|
|
||||||
assert self._x2[0].strprefix() == '[global:x2] '
|
|
||||||
py.test.raises(AttributeError, "self.log.x3")
|
|
||||||
|
|
||||||
def test_reconfig(self):
|
|
||||||
self.log.set_sub(x1=None)
|
|
||||||
self.log.x1("asdasd")
|
|
||||||
assert not self._x1
|
|
||||||
|
|
||||||
def test_reconfig_add(self):
|
|
||||||
l = []
|
|
||||||
self.log.set_sub(x2=None, x3=l.append)
|
|
||||||
self.log.x2("asdhello")
|
|
||||||
assert not self._x2
|
|
||||||
self.log.x3(123)
|
|
||||||
assert l[0].content == (123,)
|
|
||||||
|
|
||||||
def test_logger_del(self):
|
|
||||||
del self.log.x2
|
|
||||||
py.test.raises(AttributeError, "self.log.x2")
|
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
import py
|
import py
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
log = py.log.get("dynpkg",
|
log = py.log.Logger("dynpkg",
|
||||||
info=py.log.STDOUT,
|
info=py.log.STDOUT,
|
||||||
debug=py.log.STDOUT,
|
debug=py.log.STDOUT,
|
||||||
command=None) # py.log.STDOUT)
|
command=None)
|
||||||
|
|
||||||
from distutils import util
|
from distutils import util
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue