Path:
strictdoc/core/file_dependency_manager.py
Lines:
113
Non-empty lines:
93
Non-empty lines covered with requirements:
93 / 93 (100.0%)
Functions:
6
Functions covered by requirements:
6 / 6 (100.0%)
1
"""2
A simple container class for tracking dependencies between artifacts.3
4
It provides answers to whether a given artifact has to be re-generated or not.5
6
@relation(SDOC-SRS-2, scope=file)7
"""8
9
import datetime
10
import os.path
11
from copy import deepcopy
12
from dataclasses import dataclass
13
from pathlib import Path
14
from typing import Dict, Optional, Set
15
16
from strictdoc.core.project_config import ProjectConfig
17
from strictdoc.helpers.file_modification_time import (
18
get_file_modification_time,
19
)20
from strictdoc.helpers.pickle import pickle_dump, pickle_load
21
22
23
@dataclass24
class FileDependencyManager:
25
path_to_cache_dir: str
26
path_to_cache: str
27
dependencies_now: Dict[str, Set[str]]
28
dependencies_prev: Dict[str, Set[str]]
29
dependencies_must_renegerate: Set[str]
30
31
@staticmethod32
def create_from_cache(
33
project_config: ProjectConfig,
34
) -> "FileDependencyManager":
35
path_to_cache_dir = project_config.get_path_to_cache_dir()
36
path_to_cache = os.path.join(path_to_cache_dir, "dependencies")
37
38
if not os.path.isfile(path_to_cache):
39
return FileDependencyManager(
40
path_to_cache_dir, path_to_cache, {}, {}, set()
41
)42
43
with open(path_to_cache, "rb") as cache_file:
44
cache_file_bytes = cache_file.read()
45
46
unpickled_content: Optional[FileDependencyManager] = pickle_load(
47
cache_file_bytes48
)49
50
if unpickled_content is not None:
51
assert isinstance(unpickled_content, FileDependencyManager), (
52
unpickled_content,
53
)54
unpickled_content.dependencies_now.clear()
55
return unpickled_content
56
57
raise AssertionError( # pragma: no cover
58
f"Problem reading the file dependency cache file: {path_to_cache}"
59
)60
61
def must_generate(self, path_to_input_file: str) -> bool:
62
must_regenerate = (
63
path_to_input_file in self.dependencies_must_renegerate
64
)65
return must_regenerate
66
67
def add_dependency(
68
self,
69
path_to_file: str,
70
path_to_dependent_file: str,
71
) -> None:
72
dependencies = self.dependencies_now.setdefault(path_to_file, set())
73
dependencies.add(path_to_dependent_file)
74
75
def save_to_cache(self) -> None:
76
self.dependencies_prev = deepcopy(self.dependencies_now)
77
78
pickled_content = pickle_dump(self)
79
80
Path(self.path_to_cache_dir).mkdir(exist_ok=True, parents=True)
81
with open(self.path_to_cache, "wb") as cache_file:
82
cache_file.write(pickled_content)
83
84
def resolve_modification_dates(
85
self, strictdoc_last_update: datetime.datetime
86
) -> None:
87
self.dependencies_must_renegerate.clear()
88
89
items_now, items_before = (
90
self.dependencies_now.items(),
91
self.dependencies_prev.items(),
92
)93
for items_ in [items_now, items_before]:
94
for path_to_input_file_, dependencies_ in items_:
95
for path_to_dependency_ in dependencies_:
96
if (
97
# The file has not been generated yet.98
not os.path.isfile(path_to_dependency_)
99
# The file used to exist but not anymore.100
or not os.path.isfile(path_to_input_file_)
101
# The file is outdated compared to its HTML output artifact.102
or get_file_modification_time(path_to_input_file_)
103
> get_file_modification_time(path_to_dependency_)
104
# The file is outdated compared to StrictDoc's own code (this105
# branch is development-only).106
or strictdoc_last_update
107
> get_file_modification_time(path_to_dependency_)
108
):109
self.dependencies_must_renegerate.add(
110
path_to_dependency_111
)112
113
self.save_to_cache()