add: логгер

This commit is contained in:
Sergei Erjemin 2025-05-13 23:52:50 +03:00
parent b593c0148b
commit 70ddf17c9f
7 changed files with 158 additions and 16 deletions

View File

@ -1,6 +1,6 @@
| in progress // в процессе разработки |
|--------------------------------------|
| ------5 |
| -------6 |
# Типограф для Web

View File

@ -10,6 +10,7 @@ Typography - библиотека для экранной типографики
"""
__version__ = "0.1.0"
import etpgrf.defaults
from etpgrf.typograph import Typographer
from etpgrf.hyphenation import Hyphenator
import etpgrf.defaults
import etpgrf.logger

View File

@ -1,6 +1,15 @@
# etpgrf/defaults.py -- Настройки по умолчанию для типографа etpgrf
import logging
from etpgrf.config import LANG_RU, MODE_MIXED
class LoggingDefaults:
LEVEL = logging.DEBUG
FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
# Можно добавить ещё настройки, если понадобятся:
# FORMAT: str = '%(asctime)s - %(name)s - %(levelname)s - %(module)s.%(funcName)s:%(lineno)d - %(message)s'
# FILE_PATH: str | None = None # Путь к файлу лога, если None - не пишем в файл
class HyphenationDefaults:
"""
Настройки по умолчанию для Hyphenator etpgrf.
@ -16,6 +25,7 @@ class EtpgrfDefaultSettings:
def __init__(self):
self.LANGS: list[str] | str = LANG_RU
self.MODE: str = MODE_MIXED
self.logging_settings = LoggingDefaults()
self.hyphenation = HyphenationDefaults()
# self.quotes = EtpgrfQuoteDefaults()

View File

@ -1,4 +1,5 @@
import regex
import logging
from etpgrf.config import LANG_RU, LANG_RU_OLD, LANG_EN, SHY_ENTITIES, MODE_UNICODE
from etpgrf.defaults import etpgrf_settings
from etpgrf.comutil import parse_and_validate_mode, parse_and_validate_langs
@ -16,7 +17,11 @@ _RU_OLD_CONSONANTS_UPPER = frozenset(['Ѳ', # Фита (согласная)
_EN_VOWELS_UPPER = frozenset(['A', 'E', 'I', 'O', 'U', 'Æ', 'Œ'])
_EN_CONSONANTS_UPPER = frozenset(['B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z'])
# --- Настройки логирования ---
logger = logging.getLogger(__name__)
# --- Класс Hyphenator (расстановка переносов) ---
class Hyphenator:
"""Правила расстановки переносов для разных языков.
"""
@ -41,8 +46,10 @@ class Hyphenator:
self._load_language_resources_for_hyphenation()
# Определяем символ переноса в зависимости от режима
self._split_code: str = SHY_ENTITIES['SHY'][0] if self.mode == MODE_UNICODE else SHY_ENTITIES['SHY'][1]
print(f"========={self.max_unhyphenated_len}===========")
# ...
logger.debug(f"Hyphenator `__init__`. Langs: {self.langs}, Mode: {self.mode},"
f" Max unhyphenated_len: {self.max_unhyphenated_len},"
f" Min chars_per_part: {self.min_chars_per_part}")
def _load_language_resources_for_hyphenation(self):
# Определяем наборы гласных, согласных и т.д. в зависимости языков.
@ -100,13 +107,13 @@ class Hyphenator:
if len(word) <= self.max_unhyphenated_len or not any(self._is_vow(c) for c in word):
# Если слово короткое или не содержит гласных, перенос не нужен
return word
print("слово:", word, " // mode:", self.mode, " // langs:", self.langs)
logger.debug(f"Hyphenator: word: `{word}` // langs: {self.langs} // mode: {self.mode} // max_unhyphenated_len: {self.max_unhyphenated_len} // min_tail_len: {self.min_chars_per_part}")
# 2. ОБНАРУЖЕНИЕ ЯЗЫКА И ПОДКЛЮЧЕНИЕ ЯЗЫКОВОЙ ЛОГИКИ
# Поиск вхождения букв строки (слова) через `frozenset` -- O(1). Это быстрее регулярного выражения -- O(n)
# 2.1. Проверяем RU и RU_OLD (правила одинаковые, но разные наборы букв)
if (LANG_RU in self.langs or LANG_RU_OLD in self.langs) and frozenset(word.upper()) <= self._ru_alphabet_upper:
# Пользователь подключил русскую логику, и слово содержит только русские буквы
print(f"#### Applying Russian rules to: {word}")
logger.debug(f"`{word}` -- use `{LANG_RU}` or `{LANG_RU_OLD}` rules")
# Поиск допустимой позиции для переноса около заданного индекса
def find_hyphen_point_ru(word_segment: str, start_idx: int) -> int:
vow_indices = [i for i, char_w in enumerate(word_segment) if self._is_vow(char_w)]
@ -153,7 +160,7 @@ class Hyphenator:
# 2.2. Проверяем EN
elif LANG_EN in self.langs and frozenset(word.upper()) <= self._en_alphabet_upper:
# Пользователь подключил английскую логику, и слово содержит только английские буквы
print(f"#### Applying English rules to: {word}") # Для отладки
logger.debug(f"`{word}` -- use `{LANG_EN}` rules")
# --- Начало логики для английского языка (заглушка) ---
# ПРИМЕЧАНИЕ: Это очень упрощенная заглушка.
def find_hyphen_point_en(word_segment: str) -> int:
@ -175,7 +182,7 @@ class Hyphenator:
return split_word_en(word)
else:
# кстати "слова" в которых есть пробелы или другие разделители, тоже попадают сюда
print("!!!!ФИГНЯ")
logger.debug(f"`{word}` -- use `UNDEFINE` rules")
return word
@ -195,11 +202,8 @@ class Hyphenator:
hyphenated_word = self.hyp_in_word(word_to_process)
# ============= Для отладки (слова в которых появились переносы) ==================
print(f"hyp_in_text: '{word_to_process}'", end="")
if word_to_process != hyphenated_word:
print(f" -> '{hyphenated_word}'")
else:
print(" (no change)")
logger.debug(f"hyp_in_text: '{word_to_process}' -> '{hyphenated_word}'")
return hyphenated_word

121
etpgrf/logger.py Normal file
View File

@ -0,0 +1,121 @@
# etpgrf/logging_settings.py
import logging
from etpgrf.defaults import etpgrf_settings # Импортируем наш объект настроек по умолчанию
# --- Корневой логгер для всей библиотеки etpgrf ---
# Имя логгера "etpgrf" позволит пользователям настраивать
# логирование для всех частей библиотеки.
# Например, logging.getLogger("etpgrf").setLevel(logging.DEBUG)
# или logging.getLogger("etpgrf.hyphenation").setLevel(logging.INFO)
_etpgrf_init_logger = logging.getLogger("etpgrf")
# --- Настройка корневого логгера ---
def setup_library_logging():
"""
Настраивает корневой логгер для библиотеки etpgrf.
Эту функцию следует вызывать один раз (например, при импорте
основного модуля библиотеки или при первом обращении к логгеру).
"""
# Проверяем инициализацию хандлеров логера, чтобы случайно не добавлять хендлеры многократно
if not _etpgrf_init_logger.hasHandlers():
log_level_to_set = logging.WARNING # Значение по умолчанию
log_format_to_set = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' # Формат по умолчанию
fin_message: str | None = None
if hasattr(etpgrf_settings, 'logging_settings'):
if hasattr(etpgrf_settings.logging_settings, 'LEVEL'):
log_level_to_set = etpgrf_settings.logging_settings.LEVEL
if hasattr(etpgrf_settings.logging_settings, 'FORMAT') and etpgrf_settings.logging_settings.FORMAT:
log_format_to_set = etpgrf_settings.logging_settings.FORMAT
else:
# Этого не должно происходить, если defaults.py настроен правильно
fin_message= "ПРЕДУПРЕЖДЕНИЕ: etpgrf_settings.logging_settings не найдены при начальной настройке логгера."
_etpgrf_init_logger.setLevel(log_level_to_set) # Устанавливаем уровень логирования
console_handler = logging.StreamHandler() # Создаем хендлер вывода в консоль
console_handler.setLevel(log_level_to_set) # Уровень для хендлера тоже
formatter = logging.Formatter(log_format_to_set) # Создаем форматтер для вывода
console_handler.setFormatter(formatter) # Устанавливаем форматтер для хендлера
_etpgrf_init_logger.addHandler(console_handler) # Добавляем хендлер в логгер
if fin_message is not None:
# Если есть сообщение об отсутствии настроек в `etpgrf_settings`, выводим его
_etpgrf_init_logger.warning(fin_message)
_etpgrf_init_logger.debug(f"Корневой логгер 'etpgrf' инициализирован."
f" Уровень: {logging.getLevelName(_etpgrf_init_logger.getEffectiveLevel())}")
# --- Динамическое изменение уровня логирования ---
def update_etpgrf_log_level_from_settings():
"""
Обновляет уровень логирования для корневого логгера `etpgrf` и его
обработчиков, читая значение из `etpgrf_settings.logging_settings.LEVEL`.
"""
# Проверяем, что настройки логирования и уровень существуют в `defaults.etpgrf_settings`
if not hasattr(etpgrf_settings, 'logging_settings') or \
not hasattr(etpgrf_settings.logging_settings, 'LEVEL'):
_etpgrf_init_logger.warning("Невозможно обновить уровень логгера: `etpgrf_settings.logging_settings.LEVEL`"
" не найден.")
return
new_level = etpgrf_settings.logging_settings.LEVEL
_etpgrf_init_logger.setLevel(new_level)
for handler in _etpgrf_init_logger.handlers:
handler.setLevel(new_level) # Устанавливаем уровень для каждого хендлера
_etpgrf_init_logger.info(f"Уровень логирования `etpgrf` динамически обновлен на:"
f" {logging.getLevelName(_etpgrf_init_logger.getEffectiveLevel())}")
# --- Динамическое изменение формата логирования ---
def update_etpgrf_log_format_from_settings():
"""
Обновляет формат логирования для обработчиков корневого логгера etpgrf,
читая значение из etpgrf_settings.logging_settings.FORMAT.
"""
if not hasattr(etpgrf_settings, 'logging_settings') or \
not hasattr(etpgrf_settings.logging_settings, 'FORMAT') or \
not etpgrf_settings.logging_settings.FORMAT:
_etpgrf_init_logger.warning("Невозможно обновить формат логгера: `etpgrf_settings.logging_settings.FORMAT`"
" не найден или пуст.")
return
new_format_string = etpgrf_settings.logging_settings.FORMAT
new_formatter = logging.Formatter(new_format_string)
for handler in _etpgrf_init_logger.handlers:
handler.setFormatter(new_formatter) # Применяем новый форматтер к каждому хендлеру
_etpgrf_init_logger.info(f"Формат логирования для 'etpgrf' динамически обновлен на: '{new_format_string}'")
# --- Инициализация логгера при первом импорте ---
setup_library_logging()
# --- Предоставление логгеров для модулей ---
def get_logger(name: str) -> logging.Logger:
"""
Возвращает логгер для указанного имени.
Обычно используется как logging.getLogger(__name__) в модулях.
Имя будет дочерним по отношению к "etpgrf", например, "etpgrf.hyphenation".
"""
# Убедимся, что имя логгера начинается с "etpgrf." для правильной иерархии,
# если только это не сам корневой логгер.
if not name.startswith("etpgrf") and name != "etpgrf":
# Это может быть __name__ из модуля верхнего уровня, использующего библиотеку. В этом случае мы не хотим
# делать его дочерним от "etpgrf" насильно. Просто вернем логгер с именем...
# Либо можно настроить, что все логгеры, получаемые через эту функцию, должны быть частью иерархии "etpgrf"...
# Для простоты оставим так:
pass # logging_settings = logging.getLogger(name)
# Более правильный подход для модулей ВНУТРИ библиотеки etpgrf: они должны вызывать `logging.getLogger(__name__)`
# напрямую. Тогда эта функция `get_logger()` может быть и не нужна, если модули ничего не делают кроме:
# import logging
# logging_settings = logging.getLogger(__name__)
#
# Однако, если нужно централизованно получать логгеры, можно сделать, чтобы `get_logger()` всегда возвращал
# дочерний логгер:
# if not name.startswith("etpgrf."):
# name = f"etpgrf.{name}"
return logging.getLogger(name)

View File

@ -1,6 +1,9 @@
from etpgrf.comutil import parse_and_validate_mode, parse_and_validate_langs
from etpgrf.hyphenation import Hyphenator
import logging
# --- Настройки логирования ---
logger = logging.getLogger(__name__)
# --- Основной класс Typographer ---
@ -17,7 +20,6 @@ class Typographer:
self.langs: frozenset[str] = parse_and_validate_langs(langs)
# B. --- Обработка и валидация параметра mode ---
self.mode: str = parse_and_validate_mode(mode)
print("Typographer: langs:", self.langs, "// mode:", self.mode) # Для отладки
# C. --- Инициализация правила переноса ---
# Предпосылка: если вызвали типограф, значит, мы хотим обрабатывать текст и переносы тоже нужно расставлять.
# А для специальных случаев, когда переносы не нужны, пусть не ленятся и делают `hyphenation=False`.
@ -35,6 +37,7 @@ class Typographer:
# 4. Если hyphenation что-то неведомое, то игнорируем его и правило переноса выключено
self.hyphenation = None
# D. --- Конфигурация других правил---
logger.debug(f"Typographer `__init__`: langs: {self.langs}, mode: {self.mode}, hyphenation: {self.hyphenation}")
# Конвейер для обработки текста

View File

@ -1,11 +1,14 @@
import etpgrf
import logging
if __name__ == '__main__':
# --- Пример использования ---
print("\n--- Пример использования класса---\n")
etpgrf.defaults.etpgrf_settings.hyphenation.MAX_UNHYPHENATED_LEN = 8
etpgrf.defaults.etpgrf_settings.logging_settings.LEVEL = logging.DEBUG
etpgrf.logger.update_etpgrf_log_level_from_settings() # Обновляем уровень логирования из настроек
etpgrf.defaults.etpgrf_settings.logging_settings.FORMAT = '%(asctime)s - %(name)s = %(levelname)s - %(message)s'
etpgrf.logger.update_etpgrf_log_format_from_settings() # Обновляем формат логирования из настроек
# Определяем пользовательские правила переносов
hyphen_settings = etpgrf.Hyphenator(langs='ru', max_unhyphenated_len=8)