Работа расширения Ipython для vscode
Наконец-то созрел для перехода на vscode из pycharm. Пытаюсь сделать это наименее безболезненно. Для этого установил расширение ipython, которое запускает код в окне наиболее похожем на pycharm.
Столкнулся с проблемой при запуске:
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler_format = logging.Formatter(
fmt='[%(asctime)s: %(levelname)s] %(message)s')
handler.setFormatter(handler_format)
logger.addHandler(handler)
logger.debug('debug information')
В консоль выводится:
In [1]: %run "/Python/project/one.py"
[2024-11-12 06:42:21,680: DEBUG] debug information
Здесь нет никаких проблем, но если запустить код повторно, вывод будет следующий:
In [2]: %run "/Python/project/one.py"
[2024-11-12 06:44:20,336: DEBUG] debug information
[2024-11-12 06:44:20,336: DEBUG] debug information
Вывод как будто дублируется. Такое происходит только при выводе логов в консоль. Перечитал кучу информации в интернете нигде нет подобной проблемы.
Ответы (1 шт):
Открываем документацию IPython %run читаем абзац:
The file is executed in a namespace initially consisting only of
__name__=='__main__'
and sys.argv constructed as indicated. It thus sees its environment as if it were being run as a stand-alone program (except for sharing global objects such as previously imported modules). But after execution, the IPython interactive namespace gets updated with all variables defined in the program (except for __name__ and sys.argv
). This allows for very convenient loading of code for interactive work, while giving each program a ‘clean sheet’ to run in.
Но после выполнения интерактивное пространство имен IPython обновляется всеми переменными, определенными в программе (за исключением __name__
и sys.argv
).
Открываем документацию Logger и читаем абзац:
Module-Level Functions In addition to the classes described above, there are a number of module-level functions.
logging.getLogger(name=None) Return a logger with the specified name or, if name is None, return the root logger of the hierarchy. If specified, the name is typically a dot-separated hierarchical name like ‘a’, ‘a.b’ or ‘a.b.c.d’. Choice of these names is entirely up to the developer who is using logging, though it is recommended that name be used unless you have a specific reason for not doing that, as mentioned in Logger Objects.
All calls to this function with a given name return the same logger instance. This means that logger instances never need to be passed between different parts of an application.
Из интересного тут только: Все вызовы функции logging.getLogger(name)
с заданным именем возвращают один и тот же экземпляр logging.
Отсюда можно сделать 2 вывода:
- Переменные после выполнения сохраняются в интерактивном пространстве и доступны для дальнейшего использования. После перезапуска скрипта обновляются.
- Логгер же попадает в выше упомянутое исключение. Фактически не пересоздаётся.
Отсюда и проблема! Мы вызываем тот же логгер что был ранее создан, и добавляем ему еще один Handler.
Так что перед тем как добавить обработчик, можно сделать одно из двух:
- Удалить все старые обработчики. Например так:
logger.handlers.clear()
Но в документации указано, что атрибут handlers является "read-only" для логгера, и модификация этого атрибута напрямую (вызовом clear()
) может не быть безопасным решением, особенно в многопоточных приложениях. Вместо этого предлагают использовать:
logger.removeHandler(handler)
- Проверить наличие обработчика. Если он уже есть, то мы его не добавляем. hasHandlers()
if not logger.hasHandlers():
logger.addHandler(handler)