215 lines
8.8 KiB
Python
215 lines
8.8 KiB
Python
import re
|
|
from pathlib import Path
|
|
from typing import Optional, Dict, TextIO
|
|
|
|
from .constants import *
|
|
from .formatterif import FormatterIF
|
|
from .formatting_config import FormattingConfig
|
|
from .git_version_info import GitVersionInfo
|
|
from .generic_text import GenericText
|
|
from ..logger import logger
|
|
from abc import ABC, abstractmethod
|
|
from .enums import *
|
|
from datetime import *
|
|
|
|
|
|
class PyTeXFormatter(FormatterIF, ABC):
|
|
def __init__(
|
|
self,
|
|
input_file: Optional[Path] = None,
|
|
config: Optional[FormattingConfig] = None,
|
|
git_version_info: Optional[GitVersionInfo] = 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._git_version_info: Optional[GitVersionInfo] = git_version_info
|
|
self._allow_infile_config: bool = 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)
|
|
self._output_file: Optional[TextIO] = None # This may change over time in case of multiple output files
|
|
self._attribute_dict: Optional[Dict] = None
|
|
|
|
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 git_version_info(self) -> GitVersionInfo:
|
|
if self._git_version_info is None:
|
|
return GitVersionInfo()
|
|
else:
|
|
return self._git_version_info
|
|
|
|
@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 _update_attribute_dict(self):
|
|
self._attribute_dict: Dict[str, str] = {
|
|
FormatterProperty.author.value: self.config.author,
|
|
FormatterProperty.shortauthor.value: self.shortauthor,
|
|
FormatterProperty.date.value: datetime.now().strftime('%Y/%m/%d'),
|
|
FormatterProperty.year.value: datetime.now().strftime('%Y'),
|
|
FormatterProperty.raw_name.value: self.raw_name,
|
|
FormatterProperty.name.value: self.name,
|
|
FormatterProperty.version.value: self.config.version,
|
|
FormatterProperty.file_name.value: self.current_file_name,
|
|
FormatterProperty.source_file_name.value: self._input_file.name,
|
|
FormatterProperty.repo_version.value: self.git_version_info.repo_version.version,
|
|
FormatterProperty.repo_branch.value: self.git_version_info.repo_version.branch,
|
|
FormatterProperty.repo_commit.value: self.git_version_info.repo_version.commit_hash,
|
|
FormatterProperty.repo_dirty.value: self.git_version_info.repo_version.dirty,
|
|
FormatterProperty.pytex_version.value: self.git_version_info.pytex_version.version,
|
|
FormatterProperty.pytex_branch.value: self.git_version_info.pytex_version.branch,
|
|
FormatterProperty.pytex_commit.value: self.git_version_info.pytex_version.commit_hash,
|
|
FormatterProperty.pytex_dirty.value: self.git_version_info.pytex_version.dirty,
|
|
FormatterProperty.tex_type.value: self.config.tex_type.value,
|
|
FormatterProperty.tex_flavour.value: self.config.tex_flavour.value,
|
|
FormatterProperty.file_prefix.value: self.file_prefix,
|
|
FormatterProperty.description.value: self.config.description,
|
|
FormatterProperty.Tex_type.value: self.config.tex_type.value.capitalize()
|
|
}
|
|
|
|
@property
|
|
def attribute_dict(self) -> Dict:
|
|
if self._attribute_dict is None:
|
|
self._update_attribute_dict()
|
|
return self._attribute_dict
|
|
|
|
@property
|
|
def shortauthor(self) -> str:
|
|
parts = self.config.author.lower().replace('ß', 'ss').split(' ') # TODO: better non-alphanumeric handling
|
|
if len(parts) == 1:
|
|
return parts[0]
|
|
else:
|
|
return parts[0][0] + parts[-1]
|
|
|
|
@property
|
|
def raw_name(self) -> str:
|
|
parts = self._input_file.name.split('.', maxsplit=1)
|
|
if not len(parts) == 2:
|
|
raise NotImplementedError # invalid file name
|
|
else:
|
|
return parts[0]
|
|
|
|
@property
|
|
def file_prefix(self) -> str:
|
|
if self.config.naming_scheme == NamingScheme.prepend_author:
|
|
if self.config.tex_flavour == TeXFlavour.LaTeX2e:
|
|
return self.shortauthor + '@' + self.raw_name
|
|
elif self.config.tex_flavour == TeXFlavour.LaTeX3:
|
|
return self.shortauthor + '_' + self.raw_name
|
|
else:
|
|
raise NotImplementedError
|
|
else:
|
|
return self.raw_name
|
|
|
|
@property
|
|
def name(self):
|
|
if self.config.naming_scheme == NamingScheme.prepend_author:
|
|
return self.shortauthor + '-' + self.raw_name
|
|
else:
|
|
return self.raw_name
|
|
|
|
def current_file_name(self):
|
|
return self._output_file.name
|
|
|
|
def make_header(self, **kwargs) -> str:
|
|
try:
|
|
return '\n'.join(
|
|
[
|
|
'%' * 80,
|
|
self.header.format(**self.attribute_dict),
|
|
'%' * 80,
|
|
''
|
|
]
|
|
)
|
|
except KeyError:
|
|
raise NotImplementedError
|