""" Модуль с абстрактным классом парсера. """ import os from abc import ABC, abstractmethod from typing import BinaryIO from ..data_classes import ParsedDocument from .file_types import FileType class AbstractParser(ABC): """ Абстрактный класс парсера документов. Все конкретные парсеры должны наследоваться от этого класса и реализовывать метод parse. """ def __init__(self, file_types: FileType | list[FileType] | str | list[str] | None = None): """ Инициализирует парсер. Args: file_types: Поддерживаемые типы файлов. Может быть одним из: - FileType - объект перечисления - list[FileType] - список объектов перечисления - str - строка с расширением файла (с точкой, например ".xml") - list[str] - список строк с расширениями - None - если не указан, парсер не ограничен типами """ self.file_types = [] if file_types is None: return # Преобразуем одиночный FileType в список if isinstance(file_types, FileType): self.file_types = [file_types] # Преобразуем список FileType в список elif isinstance(file_types, list) and all(isinstance(ft, FileType) for ft in file_types): self.file_types = file_types # Преобразуем строку расширения в FileType elif isinstance(file_types, str): try: self.file_types = [FileType.from_extension(file_types)] except ValueError: # Если не удалось найти подходящий FileType, создаем пустой список self.file_types = [] # Преобразуем список строк расширений в список FileType elif isinstance(file_types, list) and all(isinstance(ft, str) for ft in file_types): self.file_types = [] for ext in file_types: try: self.file_types.append(FileType.from_extension(ext)) except ValueError: pass def _supported_extension(self, ext: str) -> bool: """ Проверяет, поддерживается ли расширение файла. Этот метод должен быть переопределен в наследниках для указания поддерживаемых расширений. Args: ext (str): Расширение файла с точкой (.pdf, .docx и т.д.). Returns: bool: True, если расширение поддерживается, иначе False. """ if not self.file_types: try: FileType.from_extension(ext) return True except ValueError: return False ext = ext.lower() for file_type in self.file_types: for supported_ext in file_type.value: if ext == supported_ext.lower(): return True return False def supports_file(self, file: str | BinaryIO | FileType) -> bool: """ Проверяет, может ли парсер обработать файл. Args: file: Может быть одним из: - str: Путь к файлу - BinaryIO: Объект файла - FileType: Конкретный тип файла Returns: bool: True, если парсер поддерживает файл, иначе False. """ # Если передан FileType, проверяем его наличие в списке поддерживаемых if isinstance(file, FileType): return file in self.file_types # Если переданы пустые file_types и не строка, не можем определить тип if not self.file_types and not isinstance(file, str): return False # Если передан путь к файлу, проверяем расширение if isinstance(file, str): _, ext = os.path.splitext(file) return self._supported_extension(ext) # Если передан бинарный объект, считаем что подходит # (конкретный тип будет проверен при вызове parse) return True @abstractmethod def parse(self, file: BinaryIO, file_type: FileType | None = None) -> ParsedDocument: """ Парсит документ из объекта файла и возвращает его структурное представление. Args: file (BinaryIO): Объект файла для парсинга. file_type (FileType | None): Тип файла, если известен. Returns: ParsedDocument: Структурное представление документа. """ pass @abstractmethod def parse_by_path(self, file_path: str) -> ParsedDocument: """ Парсит документ по пути к файлу и возвращает его структурное представление. Args: file_path (str): Путь к файлу для парсинга. Returns: ParsedDocument: Структурное представление документа. """ pass