Fix package structure and lazy-import ODT formatter

Add missing __init__.py to formatters/ so setuptools.find_packages
includes the subpackage in wheel/sdist builds (#16).

Move ODTFormatter import from top-level to lazy import inside
get_formatter_by_extension() so missing odfpy no longer crashes module
load before main() runs. Remove dead except ImportError handler in
cli.py save() block; surface a clear 'odfpy is not installed' message
when the formatter factory returns None for .odt (#25).

Closes #16, closes #25
This commit is contained in:
Артём Кокос
2026-06-25 20:42:13 +07:00
parent 0e4e0f3ee2
commit 3956decd4e
5 changed files with 132 additions and 16 deletions

View File

@@ -107,25 +107,23 @@ def main(argv: Optional[List[str]] = None) -> int:
)
if not formatter:
known_exts = ", ".join([".odt", ".csv", ".md", ".html"])
print(
f"❌ Неизвестный формат файла: {output_ext!r}. Поддерживаются: {known_exts}",
file=sys.stderr,
)
return 1
try:
formatter.save(rows, args.output)
print(f"✅ Report saved to {args.output}")
except ImportError as e:
if output_ext == ".odt":
print(
"❌ odfpy is not installed. Install with: pip install odfpy",
file=sys.stderr,
)
else:
print(f"❌ Import error: {e}", file=sys.stderr)
known_exts = ", ".join([".odt", ".csv", ".md", ".html"])
print(
f"❌ Неизвестный формат файла: {output_ext!r}. "
f"Поддерживаются: {known_exts}",
file=sys.stderr,
)
return 1
try:
formatter.save(rows, args.output)
print(f"✅ Report saved to {args.output}")
except Exception as e:
fmt = output_ext.lstrip(".").upper()
print(f"{fmt} export error: {e}", file=sys.stderr)

View File

View File

@@ -5,11 +5,11 @@ from .console import CompactFormatter, TableFormatter
from .csv import CSVFormatter
from .html import HTMLFormatter
from .markdown import MarkdownFormatter
from .odt import ODTFormatter
# Словарь для сопоставления расширений файлов с классами форматтеров
# Словарь для сопоставления расширений файлов с классами форматтеров.
# ODT намеренно отсутствует — его импорт отложен (ленивый), так как odfpy
# может быть не установлен. См. get_formatter_by_extension.
FORMATTER_MAP: Dict[str, Type[Formatter]] = {
".odt": ODTFormatter,
".csv": CSVFormatter,
".md": MarkdownFormatter,
".html": HTMLFormatter,
@@ -27,10 +27,23 @@ def get_formatter_by_extension(extension: str, **kwargs) -> Optional[Formatter]:
"""
Возвращает экземпляр форматтера по расширению файла.
Ключевые аргументы (**kwargs) передаются в конструктор форматтера.
Возвращает None для .odt, если odfpy не установлен.
"""
formatter_class = FORMATTER_MAP.get(extension.lower())
ext = extension.lower()
formatter_class = FORMATTER_MAP.get(ext)
if formatter_class:
return formatter_class(**kwargs)
# ODT требует odfpy — ленивый импорт, чтобы отсутствие зависимости
# не ломало загрузку модуля и другие форматтеры.
if ext == ".odt":
try:
from .odt import ODTFormatter
except ImportError:
return None
return ODTFormatter(**kwargs)
return None