chutils 2.2.0__tar.gz → 2.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {chutils-2.2.0 → chutils-2.2.1}/PKG-INFO +2 -2
- {chutils-2.2.0 → chutils-2.2.1}/pyproject.toml +5 -2
- {chutils-2.2.0 → chutils-2.2.1}/src/chutils/logger.py +59 -96
- {chutils-2.2.0 → chutils-2.2.1}/LICENSE +0 -0
- {chutils-2.2.0 → chutils-2.2.1}/README.md +0 -0
- {chutils-2.2.0 → chutils-2.2.1}/src/chutils/__init__.py +0 -0
- {chutils-2.2.0 → chutils-2.2.1}/src/chutils/config.py +0 -0
- {chutils-2.2.0 → chutils-2.2.1}/src/chutils/decorators.py +0 -0
- {chutils-2.2.0 → chutils-2.2.1}/src/chutils/secret_manager.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chutils
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: Набор простых и удобных утилит для Python, который избавляет от рутины при работе с конфигурацией и логированием в новых проектах.
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
18
|
Requires-Dist: dotenv (>=0.9.9,<0.10.0)
|
|
19
|
-
Requires-Dist: keyring (>=25.
|
|
19
|
+
Requires-Dist: keyring (>=25.7.0,<26.0.0)
|
|
20
20
|
Requires-Dist: python-dotenv (>=1.2.1,<2.0.0)
|
|
21
21
|
Requires-Dist: pyyaml (>=6.0.3,<7.0.0)
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "chutils"
|
|
3
|
-
version = "2.2.
|
|
3
|
+
version = "2.2.1"
|
|
4
4
|
description = "Набор простых и удобных утилит для Python, который избавляет от рутины при работе с конфигурацией и логированием в новых проектах."
|
|
5
5
|
authors = ["Chu4hel <sergeiivanov636@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -20,11 +20,14 @@ exclude = [
|
|
|
20
20
|
".ruff_cache/",
|
|
21
21
|
"coverage.xml",
|
|
22
22
|
".coverage",
|
|
23
|
+
"PUBLISHING.md",
|
|
24
|
+
"project.txt",
|
|
25
|
+
"changelog.txt",
|
|
23
26
|
]
|
|
24
27
|
|
|
25
28
|
[tool.poetry.dependencies]
|
|
26
29
|
python = ">=3.9"
|
|
27
|
-
keyring = "^25.
|
|
30
|
+
keyring = "^25.7.0"
|
|
28
31
|
pyyaml = "^6.0.3"
|
|
29
32
|
python-dotenv = "^1.2.1"
|
|
30
33
|
dotenv = "^0.9.9"
|
|
@@ -214,8 +214,6 @@ logging.setLoggerClass(ChutilsLogger)
|
|
|
214
214
|
|
|
215
215
|
# Кэш для пути к директории логов. Изначально пуст.
|
|
216
216
|
_LOG_DIR: Optional[str] = None
|
|
217
|
-
# Глобальный экземпляр основного логгера приложения
|
|
218
|
-
_logger_instance: Optional[ChutilsLogger] = None
|
|
219
217
|
# Флаг, чтобы сообщение об инициализации выводилось только один раз
|
|
220
218
|
_initialization_message_shown = False
|
|
221
219
|
|
|
@@ -282,82 +280,72 @@ def setup_logger(
|
|
|
282
280
|
backup_count: int = 3
|
|
283
281
|
) -> ChutilsLogger:
|
|
284
282
|
"""
|
|
285
|
-
Настраивает и возвращает
|
|
283
|
+
Настраивает и возвращает логгер, всегда обновляя его уровень.
|
|
286
284
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
285
|
+
При первом вызове для логгера с указанным именем она настраивает его
|
|
286
|
+
обработчики (для консоли и файла). При последующих вызовах она только
|
|
287
|
+
обновляет уровень логирования, не трогая обработчики, если не указан
|
|
288
|
+
`force_reconfigure=True`.
|
|
291
289
|
|
|
292
290
|
Args:
|
|
293
|
-
name: Имя логгера. `app_logger` используется
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
log_file_name: Опциональное имя файла для логирования. Если указано,
|
|
299
|
-
логгер будет писать в этот файл. Если не указано, имя файла
|
|
300
|
-
берется из конфигурационного файла ('Logging', 'log_file_name').
|
|
291
|
+
name: Имя логгера. `app_logger` используется как стандартное имя.
|
|
292
|
+
log_level: Явное указание уровня логирования (строкой или LogLevel).
|
|
293
|
+
Если не задан, значение берется из конфигурационного файла.
|
|
294
|
+
log_file_name: Имя файла для логирования. Если не указано, имя берется
|
|
295
|
+
из конфигурации ('Logging', 'log_file_name').
|
|
301
296
|
force_reconfigure: Если True, принудительно удаляет все существующие
|
|
302
297
|
обработчики и настраивает логгер заново.
|
|
303
|
-
rotation_type: Тип
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
compress: Если True, ротированные файлы логов будут сжиматься в формат .gz.
|
|
308
|
-
По умолчанию False.
|
|
309
|
-
backup_count: Количество хранимых ротированных файлов логов.
|
|
310
|
-
Старые файлы будут удаляться. По умолчанию 3.
|
|
298
|
+
rotation_type: Тип ротации: 'time' (ежедневная) или 'size'.
|
|
299
|
+
max_bytes: Максимальный размер файла для ротации по 'size'.
|
|
300
|
+
compress: Сжимать ли ротированные логи в .gz.
|
|
301
|
+
backup_count: Количество хранимых ротированных файлов.
|
|
311
302
|
|
|
312
303
|
Returns:
|
|
313
|
-
|
|
304
|
+
Настроенный экземпляр ChutilsLogger.
|
|
314
305
|
"""
|
|
315
|
-
global
|
|
316
|
-
logging.
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
306
|
+
global _initialization_message_shown
|
|
307
|
+
logger = logging.getLogger(name)
|
|
308
|
+
|
|
309
|
+
# --- 1. Определение и установка уровня логирования ---
|
|
310
|
+
# Этот блок выполняется всегда, чтобы уровень можно было изменить в любой момент.
|
|
311
|
+
cfg = config.get_config() # Загружаем конфигурацию
|
|
312
|
+
|
|
313
|
+
log_level_enum: LogLevel
|
|
314
|
+
if isinstance(log_level, str):
|
|
315
|
+
try:
|
|
316
|
+
log_level_enum = LogLevel(log_level.upper())
|
|
317
|
+
except ValueError:
|
|
318
|
+
log_level_enum = LogLevel.INFO
|
|
319
|
+
elif log_level is None:
|
|
320
|
+
level_from_config = config.get_config_value('Logging', 'log_level', 'INFO', cfg)
|
|
321
|
+
try:
|
|
322
|
+
log_level_enum = LogLevel(level_from_config.upper())
|
|
323
|
+
except ValueError:
|
|
324
|
+
log_level_enum = LogLevel.INFO
|
|
325
|
+
else:
|
|
326
|
+
log_level_enum = log_level
|
|
327
|
+
|
|
328
|
+
level_int = getattr(logging, log_level_enum.value, logging.INFO)
|
|
329
|
+
logger.setLevel(level_int)
|
|
330
|
+
logger.propagate = False
|
|
331
|
+
logging.debug("Уровень логирования для '%s' установлен на: %s (%s)", name, log_level_enum.value, level_int)
|
|
332
|
+
|
|
333
|
+
# --- 2. Настройка обработчиков (только при необходимости) ---
|
|
334
|
+
if logger.hasHandlers() and not force_reconfigure:
|
|
335
|
+
logging.debug("Обработчики для логгера '%s' уже настроены. Пропускаем настройку.", name)
|
|
336
|
+
return logger # type: ignore
|
|
329
337
|
|
|
330
338
|
# Если требуется принудительная перенастройка, очищаем старые обработчики
|
|
331
339
|
if force_reconfigure:
|
|
332
340
|
logging.debug("Принудительная перенастройка для '%s'. Удаление старых обработчиков...", name)
|
|
333
|
-
for handler in
|
|
334
|
-
handler.close()
|
|
335
|
-
|
|
341
|
+
for handler in logger.handlers[:]:
|
|
342
|
+
handler.close()
|
|
343
|
+
logger.removeHandler(handler)
|
|
336
344
|
|
|
337
|
-
#
|
|
338
|
-
if name == 'app_logger' and _logger_instance:
|
|
339
|
-
logging.debug("Возвращаем кэшированный основной логгер.")
|
|
340
|
-
return _logger_instance
|
|
341
|
-
|
|
342
|
-
# Получаем директорию для логов. Это первая точка, где запускается вся магия поиска путей.
|
|
345
|
+
# Получаем директорию для логов.
|
|
343
346
|
log_dir = _get_log_dir()
|
|
344
347
|
logging.debug("setup_logger() получил log_dir: %s", log_dir)
|
|
345
348
|
|
|
346
|
-
# Загружаем конфигурацию для получения настроек логирования.
|
|
347
|
-
cfg = config.get_config()
|
|
348
|
-
|
|
349
|
-
# Определяем уровень логирования
|
|
350
|
-
if log_level is None:
|
|
351
|
-
level_from_config = config.get_config_value('Logging', 'log_level', 'INFO', cfg)
|
|
352
|
-
try:
|
|
353
|
-
log_level = LogLevel(level_from_config.upper())
|
|
354
|
-
except ValueError:
|
|
355
|
-
log_level = LogLevel.INFO
|
|
356
|
-
|
|
357
|
-
level_int = getattr(logging, log_level.value, logging.INFO)
|
|
358
|
-
existing_logger.setLevel(level_int)
|
|
359
|
-
logging.debug("Уровень логирования для '%s' установлен на: %s (%s)", name, log_level.value, level_int)
|
|
360
|
-
|
|
361
349
|
# Определяем имя файла лога
|
|
362
350
|
if log_file_name is None:
|
|
363
351
|
log_file_name = config.get_config_value('Logging', 'log_file_name', 'app.log', cfg)
|
|
@@ -369,45 +357,25 @@ def setup_logger(
|
|
|
369
357
|
compress = config.get_config_boolean('Logging', 'compress', compress, cfg)
|
|
370
358
|
backup_count = config.get_config_int('Logging', 'log_backup_count', 3, cfg)
|
|
371
359
|
|
|
372
|
-
# Создаем и настраиваем новый экземпляр логгера
|
|
373
|
-
logger = existing_logger
|
|
374
|
-
logger.setLevel(level_int)
|
|
375
360
|
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
376
361
|
|
|
377
|
-
# Обработчик для вывода в консоль
|
|
362
|
+
# Обработчик для вывода в консоль
|
|
378
363
|
console_handler = logging.StreamHandler()
|
|
379
364
|
console_handler.setFormatter(formatter)
|
|
380
365
|
logger.addHandler(console_handler)
|
|
381
366
|
|
|
382
|
-
# Обработчик для записи в файл
|
|
383
|
-
# Добавляем его, только если директория логов была успешно определена.
|
|
367
|
+
# Обработчик для записи в файл
|
|
384
368
|
if log_dir and log_file_name:
|
|
385
|
-
|
|
386
|
-
# Это нужно для нашего отладочного теста, который работает во временной папке
|
|
387
|
-
if Path(log_file_name).is_absolute():
|
|
388
|
-
log_file_path = Path(log_file_name)
|
|
389
|
-
else:
|
|
390
|
-
log_file_path = Path(log_dir) / log_file_name
|
|
369
|
+
log_file_path = Path(log_file_name) if Path(log_file_name).is_absolute() else Path(log_dir) / log_file_name
|
|
391
370
|
logging.debug("Попытка настроить файловый обработчик для %s в %s", name, log_file_path)
|
|
392
371
|
try:
|
|
393
372
|
file_handler: Optional[logging.FileHandler] = None
|
|
394
373
|
if rotation_type == 'size':
|
|
395
374
|
handler_class = CompressingRotatingFileHandler if compress else logging.handlers.RotatingFileHandler
|
|
396
|
-
file_handler = handler_class(
|
|
397
|
-
log_file_path,
|
|
398
|
-
maxBytes=max_bytes,
|
|
399
|
-
backupCount=backup_count,
|
|
400
|
-
encoding='utf-8'
|
|
401
|
-
)
|
|
375
|
+
file_handler = handler_class(log_file_path, maxBytes=max_bytes, backupCount=backup_count, encoding='utf-8')
|
|
402
376
|
else: # 'time'
|
|
403
377
|
handler_class = CompressingTimedRotatingFileHandler if compress else SafeTimedRotatingFileHandler
|
|
404
|
-
file_handler = handler_class(
|
|
405
|
-
log_file_path,
|
|
406
|
-
when="D",
|
|
407
|
-
interval=1,
|
|
408
|
-
backupCount=backup_count,
|
|
409
|
-
encoding='utf-8'
|
|
410
|
-
)
|
|
378
|
+
file_handler = handler_class(log_file_path, when="D", interval=1, backupCount=backup_count, encoding='utf-8')
|
|
411
379
|
|
|
412
380
|
if file_handler:
|
|
413
381
|
file_handler.setFormatter(formatter)
|
|
@@ -416,18 +384,13 @@ def setup_logger(
|
|
|
416
384
|
if not _initialization_message_shown:
|
|
417
385
|
logger.debug(
|
|
418
386
|
"Логирование настроено. Уровень: %s. Файл: %s, ротация: %s, сжатие: %s.",
|
|
419
|
-
|
|
387
|
+
log_level_enum.value, log_file_path, rotation_type, compress
|
|
420
388
|
)
|
|
421
389
|
_initialization_message_shown = True
|
|
422
390
|
except Exception as e:
|
|
423
391
|
logger.error("Не удалось настроить файловый обработчик логов для %s: %s", log_file_path, e)
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
_initialization_message_shown = True
|
|
428
|
-
|
|
429
|
-
# Кэшируем основной логгер приложения
|
|
430
|
-
if name == 'app_logger':
|
|
431
|
-
_logger_instance = logger
|
|
392
|
+
elif not _initialization_message_shown:
|
|
393
|
+
logger.warning("Директория для логов не настроена. Файловое логирование отключено.")
|
|
394
|
+
_initialization_message_shown = True
|
|
432
395
|
|
|
433
396
|
return logger # type: ignore
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|