Могу ли я использовать сообщение об ошибке для классификации ошибки?
Я пишу обёртку над несколькими архивами наподобие Patool. Я хочу оповестить пользователя, если архив (какой бы он не был) требует пароль для распаковки, а также если пароль не верен. Для этого я использую пользовательские типы исключений
class WrongPasswordError(ValueError):
''' Raised when the password for unpacking the archive is incorrect. '''
class PasswordRequiredError(ValueError):
''' Raised when a password is required to unpack an archive, but it is not specified. '''
Таким образом, когда я пытаюсь распаковать Rar архив, я использую следующий перехват исключений:
def extract_file(self, file_path, output_path, password=None):
try:
if password is not None:
self.archive_obj.extract(file_path, path=output_path, pwd=password)
else:
self.archive_obj.extract(file_path, path=output_path)
return True
except rarfile.PasswordRequired as e:
raise PasswordRequiredError(str(e))
except rarfile.RarWrongPassword as e:
raise WrongPasswordError(str(e))
Это удобно, ведь библиотека rarfile предоставляет исключения для обоих таких случаев.
Но я не могу использовать тот же подход для Zip архива, ведь для обоих случаев он возвращает лишь RuntimeError, которые отличаются между собой лишь сообщением.
# фрагмент кода Zipfile:
...
if h != check_byte:
raise RuntimeError("Bad password for file %r" % zipinfo.orig_filename)
...
# другой фрагмент кода
...
if not pwd:
raise RuntimeError("File %r is encrypted, password "
"required for extraction" % name)
...
Мне приходится перехватывать это исключение и исследовать его сообщение об ошибке, чтобы разделить эти два исключения и отличить их от любых других исключений следующим образом:
def extract_file(self, file_path, output_path, password=None):
try:
if password is not None:
self.archive_obj.extract(file_path, path=output_path, pwd=password)
else:
self.archive_obj.extract(file_path, path=output_path)
except RuntimeError as e:
stringed = str(e)
if 'password required for extraction' in stringed:
raise PasswordRequiredError(stringed)
elif 'Bad password' in stringed:
raise WrongPasswordError(stringed)
else:
raise
Я полагаю, что это может вызвать проблемы с производительностью, зависит от реализации библиотеки, которая может измениться и вообще выглядит весьма непитонично и ненадёжно. Python предписывает вызывать преимущественно встроенные исключения вместо пользовательских и zipfile успешно следует этой концепции.
Есть ли более чистый и питонический способ решить проблему с разделением исключений?