Зачем ловить исключение закрыть открытый файл, с контекстом и без него? - PullRequest
3 голосов
/ 01 ноября 2019

Я работал над проблемой из небольшого сценария , который я написал. Это крюк между двумя приложениями. Проблема в том, что одно приложение было обновлено, и теперь оно использует yaml вместо json в качестве файла конфигурации.

Минимальный пример

import os 
import yaml
import json

config = {
    'version': "2.0.2",
    'journals': {
        "default": "/test/yaml/bhla"
    },
    'editor': os.getenv('VISUAL') or os.getenv('EDITOR') or "",
    'encrypt': False,
    'template': False,
    'default_hour': 9,
    'default_minute': 0,
    'timeformat': "%Y-%m-%d %H:%M",
    'tagsymbols': '@',
    'highlight': True,
    'linewrap': 79,
    'indent_character': '|',
}
with open("jrnl.yaml", 'w') as f:
    yaml.safe_dump(config, f, encoding='utf-8', allow_unicode=True, default_flow_style=False)

Это создаст файл yaml, в котором вы запустите код,

Проблема

Сначала я написал этот простой патч, чтобы мой хук мог работать с обоими (json и yaml).

JRNL_CONFIG_PATH = "jrnl.yaml"

with open(JRNL_CONFIG_PATH, "r") as f:
    try:
        JRNL_CONFIG = json.load(f)
    except json.JSONDecodeError:
        JRNL_CONFIG = yaml.load(f, Loader=yaml.FullLoader)
TAGS_SYMBOL = JRNL_CONFIG.get("tagsymbols", "@")

Но как большой сюрприз, когдаошибка поймана, f закрыта, потому что JRNL_CONFIG вернет None и выдаст эту ошибку:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-141-bc1ef847563b> in <module>()
----> 1 JRNL_CONFIG.get("tagsymbols", "@")

AttributeError: 'NoneType' object has no attribute 'get'

Вопросы

  1. Почему перехватывать исключение закрыть открытый файл, си без контекста?
  2. Каков наилучший способ отловить ошибку json и при этом иметь возможность анализировать файл как yaml?

Протестировано

  • Не удается передать файл по имени, так как файл конфигурации может не иметь расширения (.json, .yaml)
  • Эта работа, но она далека от элегантности ...
try:
    f = open(JRNL_CONFIG_PATH, "r")
    JRNL_CONFIG = json.load(f)
except json.JSONDecodeError:
    f = open(JRNL_CONFIG_PATH, "r")
    JRNL_CONFIG = yaml.load(f, Loader=yaml.FullLoader)
finally:
    f.close()

Редактировать 1

Вопрос 1 : Зачем ловить исключение после открытияфайл с контекстом и без контекста?

Удостоверился в @ jedwards

Вопрос 2 : Что лучшеспособ отловить ошибку json и все еще иметь возможность проанализировать файл как yaml?

Был обработан @ chepner

Ответы [ 3 ]

3 голосов
/ 01 ноября 2019

Проблема не в том, что файл закрывается (это не так), а в том, что указатель файла больше не находится в ожидаемом месте (в начале файла), когда вы пытаетесь использовать запасной вариант:

with open("some.yaml") as f:
    try:
        print("before", f.tell())
        data = json.load(f)
    except json.JSONDecodeError:
        print("after", f.tell())
        print("is closed:", f.closed)

Здесь метод .tell() возвращает местоположение указателя файла.

Одним из решений будет сброс указателя файла внутри блока исключений:

with open("some.yaml") as f:
    try:
        JRNL_CONFIG = json.load(f)
    except json.JSONDecodeError:
        f.seek(0)
        JRNL_CONFIG = yaml.load(f, Loader=yaml.FullLoader)
2 голосов
/ 01 ноября 2019

Нет необходимости пробовать оба json.load и yaml.load, потому что YAML - это расширенный набор JSON, и yaml.load будет анализировать все, что может json.load.

JRNL_CONFIG_PATH = "jrnl.json"

with open(JRNL_CONFIG_PATH, "r") as f:
    JRNL_CONFIG = yaml.load(f, Loader=yaml.FullLoader)

TAGS_SYMBOL = JRNL_CONFIG.get("tagsymbols", "@")
2 голосов
/ 01 ноября 2019

А как же:

with open(JRNL_CONFIG_PATH, "r") as f:
    data = f.read()
    try:
        JRNL_CONFIG = json.loads(data)
    except json.JSONDecodeError:
        JRNL_CONFIG = yaml.load(data, Loader=yaml.FullLoader)
TAGS_SYMBOL = JRNL_CONFIG.get("tagsymbols", "@")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...