StrictDoc Documentation
strictdoc/backend/sdoc/grammar_reader.py
Source file coverage
Path:
strictdoc/backend/sdoc/grammar_reader.py
Lines:
81
Non-empty lines:
68
Non-empty lines covered with requirements:
68 / 68 (100.0%)
Functions:
3
Functions covered by requirements:
3 / 3 (100.0%)
1
from typing import Optional
2
 
3
from textx import metamodel_from_str
4
 
5
from strictdoc.backend.sdoc.grammar.grammar_builder import SDocGrammarBuilder
6
from strictdoc.backend.sdoc.models.constants import GRAMMAR_MODELS
7
from strictdoc.backend.sdoc.models.document_grammar import (
8
    DocumentGrammar,
9
    DocumentGrammarWrapper,
10
)
11
from strictdoc.backend.sdoc.pickle_cache import PickleCache
12
from strictdoc.core.project_config import ProjectConfig
13
from strictdoc.helpers.cast import assert_optional_cast
14
from strictdoc.helpers.exception import StrictDocException
15
from strictdoc.helpers.file_system import file_open_read_utf8
16
from strictdoc.helpers.textx import (
17
    drop_textx_meta,
18
    preserve_source_location_data,
19
)
20
 
21
 
22
class SDocGrammarReader:
23
    meta_model = metamodel_from_str(
24
        SDocGrammarBuilder.create_grammar_grammar(),
25
        classes=GRAMMAR_MODELS + [DocumentGrammarWrapper],
26
        use_regexp_group=True,
27
    )
28
 
29
    @staticmethod
30
    def read(
31
        input_string: str, file_path: Optional[str] = None
32
    ) -> DocumentGrammar:
33
        SDocGrammarReader.meta_model.register_obj_processors(
34
            {
35
                "GrammarElement": preserve_source_location_data,
36
            }
37
        )
38
 
39
        try:
40
            grammar_wrapper: DocumentGrammarWrapper = (
41
                SDocGrammarReader.meta_model.model_from_str(
42
                    input_string, file_name=file_path
43
                )
44
            )
45
        except Exception as exc_:  # pylint: disable=broad-except
46
            raise StrictDocException(
47
                f"Could not parse file: "
48
                f"{file_path}. "
49
                f"Error: {exc_.__class__.__name__}: {exc_}"
50
            ) from exc_
51
 
52
        grammar: DocumentGrammar = grammar_wrapper.grammar
53
        grammar.parent = None
54
 
55
        # HACK:
56
        # ProcessPoolExecutor doesn't work because of non-picklable parts
57
        # of textx. The offending fields are stripped down because they
58
        # are not used anyway.
59
        drop_textx_meta(grammar)
60
 
61
        return grammar
62
 
63
    def read_from_file(
64
        self, file_path: str, project_config: ProjectConfig
65
    ) -> DocumentGrammar:
66
        unpickled_content: Optional[DocumentGrammar] = assert_optional_cast(
67
            PickleCache.read_from_cache(file_path, project_config, "grammar"),
68
            DocumentGrammar,
69
        )
70
        if unpickled_content is not None:
71
            return unpickled_content
72
 
73
        with file_open_read_utf8(file_path) as file:
74
            grammar_content = file.read()
75
 
76
        grammar: DocumentGrammar = self.read(
77
            grammar_content, file_path=file_path
78
        )
79
        PickleCache.save_to_cache(grammar, file_path, project_config, "grammar")
80
 
81
        return grammar