StrictDoc Documentation
strictdoc/backend/sdoc/pickle_cache.py
Source file coverage
Path:
strictdoc/backend/sdoc/pickle_cache.py
Lines:
86
Non-empty lines:
75
Non-empty lines covered with requirements:
75 / 75 (100.0%)
Functions:
4
Functions covered by requirements:
4 / 4 (100.0%)
1
"""
2
@relation(SDOC-SRS-95, scope=file)
3
"""
4
 
5
import hashlib
6
import os
7
from pathlib import Path
8
from typing import Any
9
 
10
from strictdoc.core.project_config import ProjectConfig
11
from strictdoc.helpers.md5 import get_file_md5
12
from strictdoc.helpers.pickle import pickle_dump, pickle_load
13
 
14
 
15
class PickleCache:
16
    @staticmethod
17
    def read_from_cache(
18
        file_path: str, project_config: ProjectConfig, content_kind: str
19
    ) -> Any:
20
        path_to_cached_file: str = PickleCache.get_cached_file_path(
21
            file_path, project_config, content_kind
22
        )
23
        if os.path.isfile(path_to_cached_file):
24
            with open(path_to_cached_file, "rb") as cache_file:
25
                unpickled_content = cache_file.read()
26
            try:
27
                return pickle_load(unpickled_content)
28
            except Exception as exception_:
29
                raise AssertionError(
30
                    "MUST NOT REACH HERE: "
31
                    f"Error when unpickling a cache file: {path_to_cached_file}. "
32
                    "To fix the issue, simply remove the cache file or the whole cache folder. "
33
                    "Please report this exception to StrictDoc developers: "
34
                    f"https://github.com/strictdoc-project/strictdoc/issues/new"
35
                ) from exception_
36
        return None
37
 
38
    @staticmethod
39
    def save_to_cache(
40
        content: Any,
41
        file_path: str,
42
        project_config: ProjectConfig,
43
        content_kind: str,
44
    ) -> None:
45
        path_to_cached_file: str = PickleCache.get_cached_file_path(
46
            file_path, project_config, content_kind
47
        )
48
        path_to_cached_file_dir: str = os.path.dirname(path_to_cached_file)
49
        Path(path_to_cached_file_dir).mkdir(parents=True, exist_ok=True)
50
        pickled_content: Any = pickle_dump(content)
51
        with open(path_to_cached_file, "wb") as cache_file:
52
            cache_file.write(pickled_content)
53
 
54
    @staticmethod
55
    def get_cached_file_path(
56
        file_path: str, project_config: ProjectConfig, content_kind: str
57
    ) -> str:
58
        path_to_tmp_dir = project_config.get_path_to_cache_dir()
59
 
60
        full_path_to_file = (
61
            file_path
62
            if os.path.isabs(file_path)
63
            else os.path.abspath(file_path)
64
        )
65
 
66
        file_md5: str = get_file_md5(file_path)
67
 
68
        # File name contains an MD5 hash of its full path to ensure the
69
        # uniqueness of the cached items. Additionally, the unique file name
70
        # contains a full path to the output root to prevent collisions
71
        # between StrictDoc invocations running against the same set of SDoc
72
        # files in parallel.
73
        unique_identifier = project_config.output_dir + full_path_to_file
74
        unique_identifier_md5 = hashlib.md5(
75
            unique_identifier.encode("utf-8")
76
        ).hexdigest()
77
        file_name = os.path.basename(full_path_to_file)
78
        file_name += "_" + unique_identifier_md5 + "_" + file_md5
79
 
80
        path_to_cached_file = os.path.join(
81
            path_to_tmp_dir,
82
            content_kind,
83
            file_name,
84
        )
85
 
86
        return path_to_cached_file