ensure file integrity for tex formatters

This commit is contained in:
Maximilian Keßler 2022-01-13 20:04:18 +01:00
parent 785eb6923f
commit f8a6f83de9
9 changed files with 65 additions and 13 deletions

View file

@ -1,11 +1,9 @@
from .build_information import BuildInfo from .build_information import BuildInfo
from .pytex_file import TexFileToFormat from .pytex_file import TexFileToFormat
from .pytex_msg import pytex_msg from .pytex_msg import pytex_msg
from .checksum import md5
__all__ = [ __all__ = [
'BuildInfo', 'BuildInfo',
'TexFileToFormat', 'TexFileToFormat',
'pytex_msg', 'pytex_msg'
'md5'
] ]

View file

@ -5,7 +5,7 @@ from PyTeX.build.git_hook import is_recent, get_latest_commit
from PyTeX import PackageFormatter, ClassFormatter, DictionaryFormatter from PyTeX import PackageFormatter, ClassFormatter, DictionaryFormatter
from PyTeX.errors import * from PyTeX.errors import *
from .pytex_msg import pytex_msg from .pytex_msg import pytex_msg
from .checksum import md5 from PyTeX.utils import md5
from .build_information import BuildInfo from .build_information import BuildInfo
@ -129,7 +129,11 @@ class TexFileToFormat:
else: else:
raise ProgrammingError raise ProgrammingError
formatter.make_default_macros() formatter.make_default_macros()
written_files = formatter.format_file(self.src_path, self.build_path) written_files = formatter.format_file(
input_path=self.src_path,
output_dir=self.build_path,
relative_name=str(self.src_path.relative_to(self.src_root)),
last_build_info=self.last_build_info_all)
build_infos = [] build_infos = []
for written_file in written_files: for written_file in written_files:
info = { info = {

View file

@ -29,7 +29,13 @@ class DictionaryFormatter(Formatter):
def expected_file_name(self): def expected_file_name(self):
return self.file_name return self.file_name
def format_file(self, input_path: Path, output_dir: Path = None) -> List[str]: def format_file(
self,
input_path: Path,
output_dir: Path = None,
relative_name: Optional[str] = None,
last_build_info: Optional[List[Dict]] = None
) -> List[str]:
self.source_file_name = str(input_path.name) self.source_file_name = str(input_path.name)
written_files = [] written_files = []

View file

@ -1,10 +1,12 @@
from .errors import PyTexError, SubmoduleDirtyForbiddenError, ProgrammingError, ExtraHeaderFileNotFoundError, \ from .errors import PyTexError, SubmoduleDirtyForbiddenError, ProgrammingError, ExtraHeaderFileNotFoundError, \
UnknownTexVersionError UnknownTexVersionError, ModifiedFileInBuildDirectoryError, UnknownFileInBuildDirectoryError
__all__ = [ __all__ = [
'PyTexError', 'PyTexError',
'SubmoduleDirtyForbiddenError', 'SubmoduleDirtyForbiddenError',
'ProgrammingError', 'ProgrammingError',
'ExtraHeaderFileNotFoundError', 'ExtraHeaderFileNotFoundError',
'UnknownTexVersionError' 'UnknownTexVersionError',
'ModifiedFileInBuildDirectoryError',
'UnknownFileInBuildDirectoryError'
] ]

View file

@ -33,3 +33,24 @@ class UnknownTexVersionError(PyTexError):
f"Unknown TeX version {tex_version}given. Only 'LaTeX2e' and 'LaTeX3' " f"Unknown TeX version {tex_version}given. Only 'LaTeX2e' and 'LaTeX3' "
f"are currently supported" f"are currently supported"
) )
class ModifiedFileInBuildDirectoryError(PyTexError):
def __init__(self, filename: str):
super().__init__(
f"File '{filename}' in the build directory has been modified since the last build. "
f"Refusing to overwrite a modified file, since you could lose your manual changes. "
f"If you are sure you do not need this anymore, delete it manually and build again. "
f"Note that for exactly this reason, it is strongly discouraged to edit built files directly."
)
class UnknownFileInBuildDirectoryError(PyTexError):
def __init__(self, filename: str):
super().__init__(
f"Unknown file {filename} in build directory found. "
f"PyTeX has no knowledge whether this file has been built by PyTeX. "
f"Refusing to overwrite this file, since you could lose your data. "
f"If you are sure, this can be got rid of, delete the file manually, "
f"and run the build again."
)

View file

@ -1,5 +1,5 @@
from pathlib import Path from pathlib import Path
from typing import List from typing import List, Dict, Optional
class Formatter: class Formatter:
@ -10,7 +10,7 @@ class Formatter:
def make_default_macros(self) -> None: def make_default_macros(self) -> None:
pass pass
def format_file(self, input_path: Path, output_dir: Path) -> List[str]: def format_file(self, input_path: Path, output_dir: Path, last_build_info: Optional[List[Dict]] = None) -> List[str]:
pass pass
def expected_file_name(self) -> str: def expected_file_name(self) -> str:

View file

@ -5,7 +5,8 @@ from typing import Dict, Optional, List
from datetime import * from datetime import *
from PyTeX.base import Attributes, Args from PyTeX.base import Attributes, Args
from PyTeX.errors import UnknownTexVersionError from PyTeX.errors import *
from PyTeX.utils import md5
from .formatter import Formatter from .formatter import Formatter
@ -112,7 +113,12 @@ class TexFormatter(Formatter):
'format_kwargs': kwargs 'format_kwargs': kwargs
} }
def format_file(self, input_path: Path, output_dir: Path = None) -> List[str]: def format_file(
self,
input_path: Path,
output_dir: Path = None,
relative_name: Optional[str] = None,
last_build_info: Optional[List[Dict]] = None) -> List[str]:
self.source_file_name = str(input_path.name) self.source_file_name = str(input_path.name)
input_file = input_path.open() input_file = input_path.open()
lines = input_file.readlines() lines = input_file.readlines()
@ -127,5 +133,15 @@ class TexFormatter(Formatter):
if output_dir is None: if output_dir is None:
output_dir = input_path.parent output_dir = input_path.parent
output_dir.mkdir(parents=True, exist_ok=True) output_dir.mkdir(parents=True, exist_ok=True)
if (output_dir / self.file_name).exists():
found = False
for info in last_build_info:
if info['source file'] == relative_name:
if not md5(output_dir / self.file_name) == info['md5sum']:
raise ModifiedFileInBuildDirectoryError(str(output_dir / self.file_name))
found = True
if not found:
raise UnknownFileInBuildDirectoryError(str(output_dir / self.file_name))
(output_dir / self.file_name).write_text(''.join(newlines)) (output_dir / self.file_name).write_text(''.join(newlines))
return [str(self.file_name)] return [str(self.file_name)]

5
utils/__init__.py Normal file
View file

@ -0,0 +1,5 @@
from. checksum import md5
__all__ = [
'md5'
]