LINUX.ORG.RU

Как словить exception от socket.timeout?

 , ,


0

4

Пишу одну небольшую программу, иконка в трее и уведомления о непрочитанных письмах в почтовом ящике. Программа уже на стадии завершения, сейчас делаю «защиту от дурака», обрабатываю некоторые распространенные ошибки, чтобы юзеру выводилось сообщение об ошибке, а не краш программы.
Конкретно застрял на вводе заведомо неправильных параметров ящика (логин, пароль, порт). Экспериментирую с подсовыванием программе вместо своего собственного почтового сервера адреса «mail.yandex.ru». Программа от этого на несколько секунд впадает в ступор, а потом вываливается со следующим выхлопом:

Traceback (most recent call last):
  File "./mail-notifier.py", line 198, in <module>
    mail_check()
  File "./mail-notifier.py", line 161, in mail_check
    if (SettingsExist() == True and Mail().testConnection() == False):
  File "./mail-notifier.py", line 142, in __init__
    self.imap = imaplib.IMAP4_SSL(settings.value("MailServer"), settings.value("Port"))
  File "/usr/lib64/python3.4/imaplib.py", line 1221, in __init__
    IMAP4.__init__(self, host, port)
  File "/usr/lib64/python3.4/imaplib.py", line 181, in __init__
    self.open(host, port)
  File "/usr/lib64/python3.4/imaplib.py", line 1234, in open
    IMAP4.open(self, host, port)
  File "/usr/lib64/python3.4/imaplib.py", line 257, in open
    self.sock = self._create_socket()
  File "/usr/lib64/python3.4/imaplib.py", line 1224, in _create_socket
    sock = IMAP4._create_socket(self)
  File "/usr/lib64/python3.4/imaplib.py", line 247, in _create_socket
    return socket.create_connection((self.host, self.port))
  File "/usr/lib64/python3.4/socket.py", line 512, in create_connection
    raise err
  File "/usr/lib64/python3.4/socket.py", line 503, in create_connection
    sock.connect(sa)
socket.timeout: timed out
Вот класс Mail(), который занимается проверкой почты ( checkMail() ) и проверкой на доступность соединения с почтовым ящиком ( testConnection () ).
class Mail():
    def __init__(self):
        self.user = settings.value("Login")
        self.password = settings.value("Password")
        socket.setdefaulttimeout(5)
        self.imap = imaplib.IMAP4_SSL(settings.value("MailServer"), settings.value("Port"))
        self.imap.login(self.user, self.password)
        
    def checkMail(self):
        self.imap.select()
        self.unRead = self.imap.search(None, 'UnSeen')
        return len(self.unRead[1][0].split())
        
    def testConnection(self):
        # This code doesn't work
        try:
            socket.create_connection(settings.value("MailServer"),settings.value("Port"),2)
            return True
        except:
            pass
            return False
Есть еще небольшая отдельная функция mail_check() которая только использует этот класс и обрабатывает полученные результаты (присваивает текст разным tooltip'ам, и тому подобные безобидные вещи). Вот она:
def mail_check():
    if (SettingsExist() == True and Mail().testConnection() == True):
        if Mail().checkMail() == 0:
            window.mailboxEmpty()
        else:
            window.mailboxFull()
    else:
        window.mailboxError()
Вопрос: почему у меня exception в методе testConnection() не срабатывает, хотя стоит только он один и ни на одну категорию ошибки не настроен, должен обрабатывать все? Мне нужно сделать так, чтобы программа не крашилась и я бы мог отправить юзеру сообщение об ошибке при работающей программе.

★★★★★

Так socket.create_connection(settings.value("MailServer"),settings.value("Port"),2) отрабатывает же нормально. Подсунь вместо mail.yandex.ru какое-нить olololo и словишь исключение. Тебе нужно как-то иначе проверять ввод пользователя.

Medar ★★★★★ ()
Ответ на: комментарий от Medar

Это слишком тупо. Я хочу представить, что юзер по забывчивости подсунет программе почтовый сервер известного почтовика. Или перепутает порт. Или введет не тот логин. Словом, нужно перебрать все типичные ошибки, чтобы программа не крашилась, а выводила что-то вроде: «Не удалось установить соединение с почтовым сервером».

Rinaldus ★★★★★ ()
Ответ на: комментарий от Rinaldus

Делал и socket.error и socket.timeout. Не словилось

OSError?

anonymous ()

почему у меня exception в методе testConnection() не срабатывает

У тебя же исключение в __init__() вываливается, не там делаешь IMAP4_SSL()

mashina ★★★★★ ()
Ответ на: комментарий от mashina

Можно поподробнее? Я эту программу пишу с этого понедельника. До этого я ничего, кроме helo world не делал. Как устранить эту ошибку?

Rinaldus ★★★★★ ()
try:
    socket.setdefaulttimeout(5)
    server = imaplib.IMAP4_SSL(host, port)
    server.login('user', 'pass')
    return True
except:
    return False

Как-то так работает.

Medar ★★★★★ ()
Ответ на: комментарий от Medar

Ну так же оно все возможное поймает, что не всегда оправдано.

FIL ★★★★ ()
Ответ на: комментарий от zunkree

А вот здесь ничего глаза не режет?

        if Mail().checkMail() == 0:
            window.mailboxEmpty()
        else:
            window.mailboxFull()

ТС написал же, что у него опыт программирования - пять дней.

shrub ★★★★★ ()
Последнее исправление: shrub (всего исправлений: 1)
Ответ на: комментарий от shrub

Тоже режет, но до этого Я просто не дочитал.

ТС написал же, что у него опыт программирования - пять дней.

ну вот Я и подсказываю

zunkree ()
Ответ на: комментарий от Medar

Не работает, все равно вываливается.

Traceback (most recent call last):
  File "./mail-notifier.py", line 198, in <module>
    mail_check()
  File "./mail-notifier.py", line 161, in mail_check
    if (SettingsExist() == True and Mail().testConnection() == True):
  File "./mail-notifier.py", line 142, in __init__
    self.imap = imaplib.IMAP4_SSL(settings.value("MailServer"), settings.value("Port"))
  File "/usr/lib64/python3.4/imaplib.py", line 1221, in __init__
    IMAP4.__init__(self, host, port)
  File "/usr/lib64/python3.4/imaplib.py", line 181, in __init__
    self.open(host, port)
  File "/usr/lib64/python3.4/imaplib.py", line 1234, in open
    IMAP4.open(self, host, port)
  File "/usr/lib64/python3.4/imaplib.py", line 257, in open
    self.sock = self._create_socket()
  File "/usr/lib64/python3.4/imaplib.py", line 1224, in _create_socket
    sock = IMAP4._create_socket(self)
  File "/usr/lib64/python3.4/imaplib.py", line 247, in _create_socket
    return socket.create_connection((self.host, self.port))
  File "/usr/lib64/python3.4/socket.py", line 512, in create_connection
    raise err
  File "/usr/lib64/python3.4/socket.py", line 503, in create_connection
    sock.connect(sa)
socket.timeout: timed out

Rinaldus ★★★★★ ()
Ответ на: комментарий от shrub

Когда буду чистить и облагораживать код перед выкладкой его на Github и объявлением версии 1.0, я это учту. Но мне не дает покоя этот баг, который крашит программу. Как вы можете оценить, насколько он критичный? Если не удастся его пофиксить, стоит ли объявлять версию 1.0?

Rinaldus ★★★★★ ()
Последнее исправление: Rinaldus (всего исправлений: 1)

Посмотри на стектрейс и на try/except, на стектрейс и на try/except.

hint: У тебя валится не в огороженном месте.

A1 ()
Ответ на: комментарий от Rinaldus

Но мне не дает покоя этот баг, который крашит программу. Как вы можете оценить, насколько он критичный?

class Mail():
    def __init__(self):
        self.user = settings.value("Login")
        self.password = settings.value("Password")
        socket.setdefaulttimeout(5)
        try:
            self.imap = imaplib.IMAP4_SSL(settings.value("MailServer"), settings.value("Port"))
            self.imap.login(self.user, self.password)
        except imaplib.IMAP4.error:
            window.тут_выводишь_сообщение_в_GUI()
shrub ★★★★★ ()
Ответ на: комментарий от shrub

Ну и ЕМНИП, init предназначен для определения переменных и настроек. Делать в нём какой-то функционал неправильно. Это нужно выносить отдельно.

Как-то так:

m = Mail()
m.login()
m.check_connection()
m.check_mail()
m.logout()

shrub ★★★★★ ()
Ответ на: комментарий от shrub

Да, так было бы логичнее. Правда, все равно вываливается с ошибкой. :( Я уже думаю забить на проверку правильности введенных данных и предупредить юзеров в README, чтобы сами проверяли правильность подключения, а программа этого не умеет и вываливается в случае неправильности введенных данных.
А создавать отдельный экземпляр класса - это обязательно?

Rinaldus ★★★★★ ()
Ответ на: комментарий от Rinaldus

Не обязательно. Тем более, если у тебя небольшая программа и один экземпляр класса.

shrub ★★★★★ ()
Ответ на: комментарий от shrub

Ну и ЕМНИП, init предназначен для определения переменных и настроек.

Чушь, это зависит от желаемой логики. А смешивать вместе трансорт и гуй точно не стоит, т.е. внутри Mail() нигде не нужно делать «window.тут_выводишь_сообщение_в_GUI()»

mashina ★★★★★ ()
Ответ на: комментарий от Rinaldus

Правда, все равно вываливается с ошибкой

Когжа же ты научишься читать бектрейс? В нём ясно написано (последняя строчка), что вываливается socket.timeout, а в предлагаемом варианте ловится imaplib.IMAP4.error.

... буду чистить и облагораживать код перед выкладкой его на Github.. Если не удастся его пофиксить, стоит ли объявлять версию 1.0?

С такой феноменальной внимательностью и после исправления конкретно этого бага не стоит всё это куда-то выкладывать. В мире как бы нет недостатка в рукожопом коде.

mashina ★★★★★ ()
Последнее исправление: mashina (всего исправлений: 1)
Ответ на: комментарий от mashina

Если except писать просто без конкретизации ошибок, просто «except: », он ведь должен в таком случае все ошибки ловить? А вот не ловит.

Rinaldus ★★★★★ ()
Последнее исправление: Rinaldus (всего исправлений: 1)
Ответ на: комментарий от shrub

Кажется, получилось. Вот как я переписал класс Mail():

class Mail():
    def __init__(self):
        socket.setdefaulttimeout(5)
        self.user = settings.value("Login")
        self.password = settings.value("Password")
        self.mailserver = settings.value("MailServer")
        self.port = settings.value("Port")
        
    def login(self):
        try:
            self.imap = imaplib.IMAP4_SSL(self.mailserver, self.port)
            self.imap.login(self.user, self.password)
            return True
        except:
            print("Login error")
            return False
        
    def checkMail(self):
        self.imap.select()
        self.unRead = self.imap.search(None, 'UnSeen')
        return len(self.unRead[1][0].split())
Функция mail_check()
def mail_check():
    if SettingsExist():
        m = Mail()
        if m.login():
            if m.checkMail()== 0:
                window.mailboxEmpty()
            else:
                window.mailboxFull()
        else:
            window.mailboxError()
testConnection() я убрал вообще, т.к его функцию выполняет метод login(). И теперь с заведомо неправильными настройками он очень долго висит при запуске, потом пишет (пока - в консоль, потом облагорожу) «Login error» и в конце концов все же запускается с ошибкой «Unable to check mail». Так что спасибо вам всем большое! Это как раз то, что я хотел.

Rinaldus ★★★★★ ()
Последнее исправление: Rinaldus (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.