开发者

Thread-safe warnings in Python

I am trying to find a good way to log a warning message but appending to it information that is only known by the caller of the function.

I think it will be clear with an example.

# log method as parameter

class Runner1(object):

    def __init__(self, log):
        self.log = log

    def run(self):
        self.log('First Warning')
        self.log('Second Warning')
        return 42


class Main1(object):

    def __init__(self):
        self._runner = Runner1(self.log)

    def log(self, message):
        print('Some object specific info: {}'.format(message))

    def run(self):
        print(self._runner.run())

e1 = Main1()
e1.run()

The Main object has a log function that adds to any message its own information before logging it. This log function is given as a parameter (in this case to a Runner object). Carrying this extra parameter all the time is extremely annoying and I would like to avoid it. There开发者_如何学编程 are usually lots of object/functions and therefore I have discarded the use of the logging method as I would need to create a different logger for each object. (Is this correct?)

I have tried to bubble the warning using the warning module:

# warning module

import warnings

class Runner2(object):

    def run(self):
        warnings.warn('First Warning')
        warnings.warn('Second Warning')
        return 42


class Main2(object):

    def __init__(self):
        self._runner = Runner2()

    def log(self, message):
        print('Some object specific info: {}'.format(message))

    def run(self):
        with warnings.catch_warnings(record=True) as ws:
            warnings.simplefilter("always")
            out = self._runner.run()
            for w in ws:
                self.log(w.message)
        print(out)

e2 = Main2()
e2.run()

But according to the docs, this is not thread safe.

Finally, I have also tried some generators:

# yield warning

class _Warning(object):

    def __init__(self, message):
        self.message = message


class Runner3(object):

    def run(self):
        yield _Warning('First Warning')
        yield _Warning('Second Warning')
        yield 42


class Main3(object):

    def __init__(self):
        self._runner = Runner3()

    def log(self, message):
        print('Some object specific info: {}'.format(message))

    def run(self):
        for out in self._runner.run():
            if not isinstance(out, _Warning):
                break
            self.log(out.message)
        print(out)


e3 = Main3()
e3.run()

But the fact that you have to modify the Runner.run to yield (instead of return) the final result is inconvenient as functions will have to be specifically changed to be used in this way (Maybe this will change in the future? Last QA in PEP255). Additionally, I am not sure if there is any other trouble with this type of implementation.

So what I am looking for is a thread-safe way of bubbling warnings that does not require passing parameters. I also would like that methods that do not have warnings remain unchanged. Adding a special construct such as yield or warning.warn to bubble the warnings would be fine.

Any ideas?


import Queue

log = Queue.Queue()
class Runner1(object):

    def run(self):
        log.put('First Warning')
        log.put('Second Warning')
        return 42

class Main1(object):

    def __init__(self):
        self._runner = Runner1()

    def log(self, message):
        print('Some object specific info: {0}'.format(message))

    def run(self):
        out=self._runner.run()
        while True:
            try:
                msg = log.get_nowait()
                self.log(msg)
            except Queue.Empty:
                break
        print(out)


e1 = Main1()
e1.run()

yields

Some object specific info: First Warning
Some object specific info: Second Warning
42
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜