Потокобезопасные предупреждения в Python - PullRequest
3 голосов
/ 01 сентября 2011

Я пытаюсь найти хороший способ записать предупреждающее сообщение, но добавляю к нему информацию, известную только вызывающей функции.

Я думаю, это будет понятно на примере.

# 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()

Главный объект имеет функцию журнала, которая добавляет к любому сообщению свою собственную информацию перед его регистрацией. Эта функция журнала задается как параметр (в данном случае для объекта Runner). Ношение этого дополнительного параметра все время чрезвычайно раздражает, и я хотел бы избежать этого. Обычно есть много объектов / функций, и поэтому я отказался от использования метода ведения журнала, так как мне нужно было бы создать разные средства ведения журнала для каждого объекта. (Это правильно?)

Я попытался выдать предупреждение с помощью модуля предупреждения:

# 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()

Но, согласно документации, это не безопасно для потоков.

Наконец, я также попробовал некоторые генераторы:

# 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()

Но тот факт, что вам нужно изменить Runner.run, чтобы получить (вместо возврата) конечный результат, неудобен, так как функции должны быть специально изменены для использования таким образом (возможно, это изменится в будущем? Последний QA в PEP255 ). Кроме того, я не уверен, есть ли какие-либо другие проблемы с этим типом реализации.

Итак, я ищу поточно-ориентированный способ вывода предупреждений, который не требует передачи параметров. Также хотелось бы, чтобы методы, у которых нет предупреждений, остались без изменений. Добавление специальной конструкции, такой как yield или warning.warn, для всплытия предупреждений будет в порядке.

Есть идеи?

1 Ответ

2 голосов
/ 01 сентября 2011
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()

выходы

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