import re from pathlib import Path from typing import Optional from .constants import * from .enums import TeXType, TeXFlavour from .formatterif import FormatterIF from .formatting_config import FormattingConfig from .generic_text import GenericText from ..logger import logger class PyTeXFormatter(FormatterIF): def __init__( self, input_file: Optional[Path] = None, config: Optional[FormattingConfig] = None, tex_type: Optional[TeXType] = None, tex_flavour: Optional[TeXFlavour] = None, locate_file_config: bool = True, allow_infile_config: bool = True ): super().__init__( input_file=input_file, config=config ) self._config: Optional[FormattingConfig] = self._config # for type-hinting self._tex_type: Optional[TeXType] = tex_type self._tex_flavour: Optional[TeXFlavour] = tex_flavour self._allow_infile_config = allow_infile_config self._header: Optional[GenericText] = None if locate_file_config: file_config = self.parse_file_config() if allow_infile_config: infile_config = self.parse_infile_config() self._config = \ file_config.merge_with( infile_config, strict=True ).merge_with(self._config, strict=False) else: self._config = file_config.merge_with(self._config) else: if allow_infile_config: infile_config = self.parse_infile_config() self._config = infile_config.merge_with(self._config) def parse_file_config(self) -> Optional[FormattingConfig]: config_file = self.input_file.with_name(self.input_file.name + PYTEX_CONFIG_FILE_EXTENSION) if config_file.exists(): with open(config_file, 'r') as file: config = file.readlines() try: return FormattingConfig.from_yaml(config) except: raise NotImplementedError # Invalid yaml file format else: return None def parse_infile_config(self) -> Optional[FormattingConfig]: with open(self._input_file, "r") as file: line = file.readline() if re.match(self.config.escape_character + INFILE_CONFIG_BEGIN_CONFIG, line): if not line.strip().lstrip('%').strip() == self.config.escape_character + INFILE_CONFIG_BEGIN_CONFIG: logger.warning( "File {file}: Start of infile config invalid." ) config = [] while True: line = file.readline() if re.match(self.config.escape_character + INFILE_CONFIG_END_CONFIG, line): if not line.strip().lstrip( '%').strip() == self.config.escape_character + INFILE_CONFIG_END_CONFIG: logger.warning( "File {file}: End of infile config invalid." ) break if line == '': raise NotImplementedError # No matching end block config.append(line.lstrip('%').rstrip()) try: return FormattingConfig.from_yaml(config) except: raise NotImplementedError # Invalid yaml file format else: return None @property def config(self) -> FormattingConfig: if self._config is None: raise NotImplementedError return self._config @property def header(self) -> GenericText: if self._header is None: if not ( self.config.include_extra_header or self.config.include_build_time or self.config.include_pytex_version or self.config.include_pytex_info_text or self.config.include_repo_version or self.config.include_repo_info_text ): self._header = GenericText([]) else: self._header = GenericText([]) # TODO: handle license if self.config.include_extra_header: self._header += self.config.extra_header + [''] if self.config.include_repo_info_text: self._header += self.config.repo_info_text if self.config.include_pytex_info_text: self._header += self.config.pytex_info_text + [''] ## TODO handle rest return self._header def make_header(self, **kwargs) -> str: return '\n'.join( [ '%' * 80, self.header.format(**kwargs), # TODO: add standard keywords here '%' * 80 ] )