Фактически, эти являются фатальными ошибками - по крайней мере, поскольку они способны воспроизводить правильную игру; с другой стороны, возможно, игрок действительно совершил незаконный ход, и в то время никто не заметил (что сделало бы это предупреждение, а не фатальной ошибкой).
Учитывая возможность как фатальных ошибок (файл поврежден) и предупреждения (был сделан незаконный ход, но последующие шаги показывают согласованность с этим движением (другими словами, ошибка пользователя и никто не поймал его в то время)). Я рекомендую комбинацию первого и второго вариантов:
] Если вы не сталкиваетесь с фатальной ошибкой, вы можете вернуть игру, а также любые предупреждения / нефатальные ошибки в конце:
return game, warnings, errors
Но что, если вы нажмете фатальную ошибку?
Нет проблем: создайте настраиваемое исключение, к которому вы можете прикрепить полезную часть игры и любые другие предупреждения / нефатальные ошибки:
raise ParsingError(
'error explanation here',
game=game,
warnings=warnings,
errors=errors,
)
, затем, когда вы поймаете ошибка, с которой вы можете получить доступ (/ g10)
Пользовательская ошибка может быть:
class ParsingError(Exception):
def __init__(self, msg, game, warnings, errors):
super().__init__(msg)
self.game = game
self.warnings = warnings
self.errors = errors
и используется:
try:
first_game, warnings, errors = chess.pgn.read_game(pgn_file)
except chess.pgn.ParsingError as err:
first_game = err.game
warnings = err.warnings
errors = err.errors
# whatever else you want to do to handle the exception
Это похоже на то, как модуль subprocess
обрабатывает ошибки.
Для возможности извлечения и анализа последующих игр после фатальной ошибки игры я предлагаю изменить ваш API:
Таким образом, если у вас есть файл из пяти игр и две игры, вы все равно можете попытаться разыгрывают игры 3, 4 и 5.
Я предложил щедрость, потому что я хотел бы знать, действительно ли это лучший способ сделать это. Тем не менее, я также пишу парсер, и поэтому мне нужна эта функциональность, и это то, что я придумал.
Модуль warnings
именно то, что вы хотите.
Сначала меня отвратило то, что каждый пример предупреждения, используемого в документах, выглядит как этих :
Traceback (most recent call last):
File "warnings_warn_raise.py", line 15, in <module>
warnings.warn('This is a warning message')
UserWarning: This is a warning message
... что нежелательно, потому что я не хочу, чтобы он был UserWarning
, мне нужно собственное собственное имя предупреждения.
Вот решение этого:
import warnings
class AmbiguousStatementWarning(Warning):
pass
def x():
warnings.warn("unable to parse statement syntax",
AmbiguousStatementWarning, stacklevel=3)
print("after warning")
def x_caller():
x()
x_caller()
, который дает:
$ python3 warntest.py
warntest.py:12: AmbiguousStatementWarning: unable to parse statement syntax
x_caller()
after warning
Без библиотек трудно сделать это чисто, но все же возможно.
В зависимости от ситуации существуют различные методы обработки.
Метод 1:
Поместить все содержимое цикла while в следующее:
while 1:
try:
#codecodecode
except Exception as detail:
print detail
Способ 2:
То же, что и метод 1, за исключением нескольких попыток / исключений, так что это не пропускает слишком много кода и amp; вы знаете точное местоположение ошибки.
Извините, в спешке, надеюсь, что это поможет!
Самый Pythonic путь - это модуль logging . Это было упомянуто в комментариях, но, к сожалению, без особого упорства. Существует много причин, которые предпочтительнее предупреждений :
Основной пример использования для модуля протоколирования будет выглядеть следующим образом:
import logging
logger = logging.getLogger(__name__) # module-level logger
# (tons of code)
logger.warning('illegal move: %s in file %s', move, file_name)
# (more tons of code)
Это приведет к печати таких сообщений, как:
WARNING:chess_parser:illegal move: a2-b7 in file parties.pgn
(при условии, что ваш модуль называется chess_parser.py)
Самое главное, что вам не нужно ничего делать в вашем модуле парсера. Вы заявляете, что используете систему ведения журнала, вы используете логгер с определенным именем (то же самое, что и имя модуля анализатора в этом примере), и вы отправляете ему сообщения об уровне предупреждений. Ваш модуль не должен знать, как эти сообщения обрабатываются, отформатируются и сообщаются пользователю. Или, если они вообще сообщаются. Например, вы можете настроить модуль регистрации (обычно в самом начале вашей программы), чтобы использовать другой формат и выгрузить его в файл:
logging.basicConfig(filename = 'parser.log', format = '%(name)s [%(levelname)s] %(message)s')
И вдруг без каких-либо изменений кода вашего модуля, ваши предупреждающие сообщения сохраняются в файл с другим форматом, а не на печать:
chess_parser [WARNING] illegal move: a2-b7 in file parties.pgn
Или вы можете подавить предупреждения, если хотите:
logging.basicConfig(level = logging.ERROR)
И ваш предупреждения модуля будут полностью игнорироваться, в то время как любые сообщения ERROR или более высокого уровня из вашего модуля будут обрабатываться.
Я не уверен, является ли решение питоновым или нет, но я использую его довольно часто с небольшими изменениями: парсер выполняет свою работу в генераторе и дает результаты и код состояния. Код приема принимает решения о том, что делать с несостоявшимися элементами:
def process_items(items)
for item in items:
try:
#process item
yield processed_item, None
except StandardError, err:
yield None, (SOME_ERROR_CODE, str(err), item)
for processed, err in process_items(items):
if err:
# process and log err, collect failed items, etc.
continue
# further process processed
. Более общий подход - практиковать использование шаблонов проектирования. Упрощенная версия Observer (при регистрации обратных вызовов для определенных ошибок) или вид Visitor (где у посетителя есть методы для обработки определенных ошибок, см. SAX для анализа) может быть ясным и понятным решением.