From 5b08f6792a9252f00fb0903fb5920d3b51af54cd Mon Sep 17 00:00:00 2001 From: Mikan <72257910+Mikan-DS@users.noreply.github.com> Date: Mon, 8 Dec 2025 17:27:26 +0300 Subject: [PATCH] =?UTF-8?q?[AI]=20=D0=9F=D0=BE=D0=B4=D0=B4=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=BA=D0=B0=20=D0=BF=D0=BB=D0=B0=D0=B3=D0=B8=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__main__.py | 3 ++- src/core/converter.py | 10 ++++++++-- src/core/plugin_loader.py | 16 ++++++++++++++++ tests/test_plugins.py | 26 ++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 src/core/plugin_loader.py create mode 100644 tests/test_plugins.py diff --git a/src/__main__.py b/src/__main__.py index 93582f0..7b6f602 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -15,9 +15,10 @@ def convert( style_config: Path = typer.Option("resources/style_config.yaml", "--style", "-s", help="Файл стилей"), template: Optional[Path] = typer.Option(None, "--template", "-t", help="Шаблон .dotx"), theme: Optional[str] = typer.Option(None, "--theme", "-T", help="Название темы (gost, academic...)"), + plugins_dir: Optional[Path] = typer.Option(None, "--plugins", "-p", help="Папка с плагинами"), verbose: bool = typer.Option(False, "--verbose", "-v", help="Включить отладочные сообщения"), ): log_level = logging.DEBUG if verbose else logging.INFO logging.basicConfig(level=log_level, format='%(levelname)s: %(message)s') - process_document(input_dir, output_dir, style_config, template, theme) \ No newline at end of file + process_document(input_dir, output_dir, style_config, template, theme, plugins_dir) \ No newline at end of file diff --git a/src/core/converter.py b/src/core/converter.py index ff1b921..fc60d9f 100644 --- a/src/core/converter.py +++ b/src/core/converter.py @@ -4,12 +4,18 @@ from typing import Optional from docx import Document from .style_registry import StyleRegistry from .renderer import render_markdown_to_docx - +from .plugin_loader import load_plugins_from_path import logging logger = logging.getLogger(__name__) -def process_document(input_dir: Path, output_dir: Path, style_config_path: Path, template_path: Optional[Path] = None, theme_name: Optional[str] = None): +def process_document( + input_dir: Path, output_dir: Path, style_config_path: Path, + template_path: Optional[Path] = None, theme_name: Optional[str] = None, + plugins_dir: Optional[Path] = None +): + if plugins_dir and plugins_dir.exists(): + load_plugins_from_path(plugins_dir) if theme_name: theme_path = Path("resources/themes") / f"{theme_name}.yaml" if theme_path.exists(): diff --git a/src/core/plugin_loader.py b/src/core/plugin_loader.py new file mode 100644 index 0000000..4bf6ac8 --- /dev/null +++ b/src/core/plugin_loader.py @@ -0,0 +1,16 @@ +import importlib.util +from pathlib import Path +from ..handlers import register_handler +import logging + +logger = logging.getLogger(__name__) + + +def load_plugins_from_path(plugins_dir: Path): + """Загружает все .py файлы из папки как плагины.""" + for py_file in plugins_dir.glob("*.py"): + spec = importlib.util.spec_from_file_location(py_file.stem, py_file) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + # Теперь все @register_handler в module выполнены + logger.info(f"Загружен плагин: {py_file.name}") \ No newline at end of file diff --git a/tests/test_plugins.py b/tests/test_plugins.py new file mode 100644 index 0000000..9237570 --- /dev/null +++ b/tests/test_plugins.py @@ -0,0 +1,26 @@ +from pathlib import Path + + +def test_load_plugins(): + # Создаём плагин + plugin_dir = Path("tests/plugins") + plugin_dir.mkdir(exist_ok=True) + plugin_path = plugin_dir / "test_plugin.py" + plugin_path.write_text(""" +from src.handlers import register_handler + +@register_handler("admonition") +def handle_admonition(node, doc, images_dir, style_reg): + p = doc.add_paragraph("Тест плагина") +""", encoding="utf-8") + + # Загружаем + from src.core.plugin_loader import load_plugins_from_path + load_plugins_from_path(plugin_dir) + + # Проверяем, что обработчик зарегистрирован + from src.handlers import get_handler + handler = get_handler("admonition") + assert handler is not None + + plugin_path.unlink(missing_ok=True) \ No newline at end of file