Правильно обрабатывать ошибки, используя сторожевой таймер - PullRequest
0 голосов
/ 10 января 2019

Я пытаюсь понять, как правильно использовать обработку ошибок в Python. Я заставляю Watchdog заглядывать в мои болельщики на диск, подключенный к сети. Иногда диск отключается, а затем снова подключается, и появляется сообщение об ошибке. «Исключение в потоке Thread-2:»

У меня есть ошибка Handeler, но я не уверен, что я делаю это правильно.

Должен ли я сделать еще одну попытку на шаге наблюдающего.скрипт?

Python 3.6, Windows 10

if __name__ == '__main__':
    path = "P:\\03_auto\\Indata"
    observer = Observer()
    observer.schedule(MyHandler(), path, recursive=True)
    observer.start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()

    observer.join()

Ответы [ 3 ]

0 голосов
/ 09 июня 2019

Я думаю super().run() не очень хороший ответ. Я много чего пробовал с ответом Шми, но он не был конкретным и бессмысленным.

Ниже мой ответ. Я закончил тест.

from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler


class Watcher :
    def __init__(self):
        self.observer = Observer()
        print('observer init...')

    def run(self):
        global osv_status
        try :            
            event_handler = MyHandler(patterns=["*.txt"])
            self.observer.schedule(event_handler, Directory, recursive=True)
            self.observer.start()
            osv_status = 1            

        except OSError as ex:
            print("OSError")
            time.sleep(.5)

            if self.observer.is_alive() is True:
                self.observer.stop()
                self.observer.join()
                print("Observer is removed [ ",osv_status," ]")
                osv_status = 2

        except Exception as ex:
            self.logger.exception("Exception ex :{0} ".format(ex))


class MyHandler(PatternMatchingEventHandler):
    def __init__(self,*args, **kwargs):
        super(MyHandler, self).__init__(*args, **kwargs)
        print("MyHandler Init")

    def on_created(self, event): 
        print('New file is created ',event.src_path)

        except Exception as ex:            
            self.logger.exception("Exception ex :{0} ".format(ex))


    def on_modified(self,event):
        print('File is modified ',event.src_path)
        try :
            doing()
        except Exception as ex:            
            self.logger.exception("Exception ex :{0} ".format(ex))


if __name__ == "__main__":
    .....
    osv_status = 0 # 0: ready, 1:start  2: halt
    wch =Watcher()
    wch.run()

    try :
        while(1):
            if is_Connect == False:
                sock_connect()
                print('sock_connect')

            time.sleep(1)
            if os.path.exists(Directory) is False and osv_status == 1:
                wch.observer.stop()
                wch.observer.join()
                print("Observer is removed [ ",osv_status," ]")
                osv_status = 0
            elif os.path.exists(Directory) is True and (osv_status == 0 or osv_status == 2):
                if wch.observer.is_alive() is False:
                    wch =Watcher()
                    wch.run()

0 голосов
/ 11 июня 2019

Я также разместил это в соответствующей теме здесь

Вот как я решил эту проблему:

    from watchdog import observers
    from watchdog.observers.api import DEFAULT_OBSERVER_TIMEOUT, BaseObserver


    class MyEmitter(observers.read_directory_changes.WindowsApiEmitter):
        def queue_events(self, timeout):
            try:
                super().queue_events(timeout)
            except OSError as e:
                print(e)
                connected = False
                while not connected:
                    try:
                        self.on_thread_start()  # need to re-set the directory handle.
                        connected = True
                        print('reconnected')
                    except OSError:
                        print('attempting to reconnect...')
                        time.sleep(10)


    observer = BaseObserver(emitter_class=MyEmitter, timeout=DEFAULT_OBSERVER_TIMEOUT)
    ...

Подклассы WindowsApiEmitter для отлова исключения в queue_events. Чтобы продолжить после повторного подключения, сторожевой таймер должен переустановить дескриптор каталога, что мы можем сделать с self.on_thread_start().

Затем используйте MyEmitter с BaseObserver, теперь мы можем справиться с потерей и восстановлением соединения с общими дисками.

0 голосов
/ 10 января 2019

Хорошо, подход sys.excepthook, о котором я упоминал в своем комментарии, на данный момент неосуществим. Существует более чем десятилетняя ошибка , из-за которой потоки, полученные из threading.Thread, игнорируют sys.excepthook. В ветке сообщений об этой ошибке описаны некоторые обходные пути, но я не решаюсь опубликовать ответ, используя обходной путь, тем более что эта ошибка, наконец, исправлена ​​в Python 3.8.

Другой вариант - получить пользовательский Observer из Watchdog's Observer. Самый простой способ, которым я могу придумать, - это обертка вокруг метода run() родителя:

class CustomObserver(Observer):

    def run(self):
        while self.should_keep_running():
            try:
                # Tweak the super call if you require compatibility with Python 2
                super().run()
            except OSError: 
            # You did not mention the excpetion class in your post.
            # Be specific about what you want to handle here
                # give the file system some time to recover
                time.sleep(.5)

Судя по быстрому взгляду на источник, все наблюдатели, похоже, наследуют свои run от EventDispatcher.run(), так что вы, вероятно, даже можете опустить обертку и переопределить этот метод напрямую

class CustomObserver(Observer):

    def run(self):
        while self.should_keep_running():
            try:
                self.dispatch_events(self.event_queue, self.timeout)
            except queue.Empty:
                continue
            except OSError:
                time.sleep(.5)

Однако у меня не установлен этот пакет на моей коробке, так что эти вещи не проверены; вам, возможно, придется немного повозиться, чтобы все началось. Да, и обязательно замените OSError* на любой класс исключений, который действительно возникает в вашем случае:)

Изменить:
* В соответствии с комментарием @ HenryYik ниже, отключение сетевого диска, похоже, вызывает OSError (WinError 64: ERROR_NETNAME_DELETED) в системах Windows. Я считаю вполне вероятным, что ОС в стиле UNIX в той же ситуации вызывают исключение того же типа, поэтому я обновил фрагменты кода, чтобы теперь использовать OSError вместо FileNotFoundError, который я использовал изначально.

...