import re from pathlib import Path from typing import Optional from .constants import * from .formatterif import FormatterIF from .formatting_config import FormattingConfig from .generic_text import GenericText from ..logger import logger from abc import ABC class PyTeXFormatter(FormatterIF, ABC): def __init__( self, input_file: Optional[Path] = None, config: Optional[FormattingConfig] = 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._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) -> FormattingConfig: config_file = self.input_file.with_name(self.input_file.name + PYTEX_CONFIG_FILE_EXTENSION) if config_file.exists(): try: return FormattingConfig.from_yaml(config_file) except: raise NotImplementedError # Invalid yaml file format else: return FormattingConfig() def parse_infile_config(self) -> FormattingConfig: if self._input_file is None: raise NotImplementedError # no file initialised yet 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('\n'.join(config)) except: raise NotImplementedError # Invalid yaml file format else: return FormattingConfig() @property def config(self) -> FormattingConfig: if self._config is None: return FormattingConfig() return self._config @config.setter def config(self, formatting_config: FormattingConfig): self._config = formatting_config @property def header(self) -> GenericText: if self._header is None: if not ( self.config.include_extra_header or self.config.include_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 ] )