Path:
strictdoc/backend/sdoc/models/document_grammar.py
Lines:
421
Non-empty lines:
381
Non-empty lines covered with requirements:
381 / 381 (100.0%)
Functions:
15
Functions covered by requirements:
15 / 15 (100.0%)
1
"""2
@relation(SDOC-SRS-19, scope=file)3
"""4
5
from typing import Dict, List, Optional, Set
6
7
from strictdoc.backend.sdoc.models.grammar_element import (
8
GrammarElement,
9
GrammarElementFieldString,
10
GrammarElementFieldType,
11
)12
from strictdoc.backend.sdoc.models.model import (
13
RequirementFieldName,
14
SDocDocumentIF,
15
SDocGrammarIF,
16
)17
18
19
class DocumentGrammar(SDocGrammarIF):
20
def __init__(
21
self,
22
parent: Optional[SDocDocumentIF],
23
elements: List[GrammarElement],
24
import_from_file: Optional[str] = None,
25
) -> None:
26
self.parent: Optional[SDocDocumentIF] = parent
27
self.elements: List[GrammarElement] = elements
28
29
self.registered_elements: Set[str] = set()
30
self.elements_by_type: Dict[str, GrammarElement] = {}
31
32
self.update_with_elements(elements)
33
34
# The textX parser passes an empty string instead of None when35
# import_from_file is not provided in input.36
if import_from_file is not None and len(import_from_file) == 0:
37
import_from_file = None
38
self.import_from_file: Optional[str] = import_from_file
39
40
self.is_default = False
41
42
self.ng_line_start: Optional[int] = None
43
self.ng_col_start: Optional[int] = None
44
45
@staticmethod46
def create_default(
47
parent: Optional[SDocDocumentIF],
48
enable_mid: bool = False,
49
include_child_relation: bool = False,
50
) -> "DocumentGrammar":
51
"""
52
@relation(SDOC-SRS-93, scope=function)53
"""54
55
text_element: GrammarElement = (
56
DocumentGrammar.create_default_text_element(enable_mid=enable_mid)
57
)58
59
# @relation(SDOC-SRS-132, scope=range_start)60
fields: List[GrammarElementFieldType] = []
61
62
if enable_mid:
63
fields.append(
64
GrammarElementFieldString(
65
parent=None,
66
title=RequirementFieldName.MID,
67
human_title=None,
68
required="False",
69
)70
)71
72
fields.extend(
73
[74
GrammarElementFieldString(
75
parent=None,
76
title=RequirementFieldName.UID,
77
human_title=None,
78
required="False",
79
),80
GrammarElementFieldString(
81
parent=None,
82
title=RequirementFieldName.LEVEL,
83
human_title=None,
84
required="False",
85
),86
GrammarElementFieldString(
87
parent=None,
88
title=RequirementFieldName.STATUS,
89
human_title=None,
90
required="False",
91
),92
GrammarElementFieldString(
93
parent=None,
94
title=RequirementFieldName.TAGS,
95
human_title=None,
96
required="False",
97
),98
GrammarElementFieldString(
99
parent=None,
100
title=RequirementFieldName.TITLE,
101
human_title=None,
102
required="False",
103
),104
GrammarElementFieldString(
105
parent=None,
106
title=RequirementFieldName.STATEMENT,
107
human_title=None,
108
required="False",
109
),110
GrammarElementFieldString(
111
parent=None,
112
title=RequirementFieldName.RATIONALE,
113
human_title=None,
114
required="False",
115
),116
GrammarElementFieldString(
117
parent=None,
118
title=RequirementFieldName.COMMENT,
119
human_title=None,
120
required="False",
121
),122
]123
)124
125
requirement_element = GrammarElement(
126
parent=None,
127
tag="REQUIREMENT",
128
property_is_composite="",
129
property_prefix="",
130
property_view_style="",
131
fields=fields,
132
relations=[],
133
)134
# @relation(SDOC-SRS-132, scope=range_end)135
136
requirement_element.relations = GrammarElement.create_default_relations(
137
requirement_element,
138
include_child=include_child_relation,
139
)140
141
elements: List[GrammarElement] = []
142
143
section_element: GrammarElement = (
144
DocumentGrammar.create_default_section_element(
145
enable_mid=enable_mid
146
)147
)148
elements.append(section_element)
149
150
elements.append(text_element)
151
elements.append(requirement_element)
152
153
grammar = DocumentGrammar(
154
parent=parent, elements=elements, import_from_file=None
155
)156
grammar.is_default = True
157
text_element.parent = grammar
158
requirement_element.parent = grammar
159
160
return grammar
161
162
@staticmethod163
def create_for_test_report(parent: SDocDocumentIF) -> "DocumentGrammar":
164
section_element: GrammarElement = (
165
DocumentGrammar.create_default_section_element(
166
parent=None, enable_mid=parent.config.enable_mid == True
167
)168
)169
text_element: GrammarElement = (
170
DocumentGrammar.create_default_text_element(
171
parent=None, enable_mid=parent.config.enable_mid == True
172
)173
)174
175
fields: List[GrammarElementFieldType] = [
176
GrammarElementFieldString(
177
parent=None,
178
title=RequirementFieldName.UID,
179
human_title=None,
180
required="False",
181
),182
GrammarElementFieldString(
183
parent=None,
184
title="TEST_PATH",
185
human_title=None,
186
required="False",
187
),188
GrammarElementFieldString(
189
parent=None,
190
title="TEST_FUNCTION",
191
human_title=None,
192
required="False",
193
),194
GrammarElementFieldString(
195
parent=None,
196
title="DURATION",
197
human_title=None,
198
required="False",
199
),200
GrammarElementFieldString(
201
parent=None,
202
title=RequirementFieldName.STATUS,
203
human_title=None,
204
required="True",
205
),206
GrammarElementFieldString(
207
parent=None,
208
title=RequirementFieldName.TITLE,
209
human_title=None,
210
required="True",
211
),212
GrammarElementFieldString(
213
parent=None,
214
title=RequirementFieldName.STATEMENT,
215
human_title=None,
216
required="False",
217
),218
]219
requirement_element = GrammarElement(
220
parent=None,
221
tag="TEST_RESULT",
222
property_is_composite="",
223
property_prefix="",
224
property_view_style="",
225
fields=fields,
226
relations=[],
227
)228
229
requirement_element.relations = GrammarElement.create_default_relations(
230
requirement_element231
)232
233
elements: List[GrammarElement] = [
234
section_element,
235
text_element,
236
requirement_element,
237
]238
grammar = DocumentGrammar(
239
parent=parent, elements=elements, import_from_file=None
240
)241
grammar.is_default = True
242
section_element.parent = grammar
243
text_element.parent = grammar
244
requirement_element.parent = grammar
245
246
return grammar
247
248
def get_element_by_mid(self, element_mid: str) -> GrammarElement:
249
for element_ in self.elements:
250
if element_.mid == element_mid:
251
return element_
252
raise AssertionError(
253
f"Could not find a grammar element with MID: {element_mid}"
254
)255
256
def dump_fields(self, node_type: str) -> str:
257
return ", ".join(
258
list(
259
map(
260
lambda g: g.title,
261
self.elements_by_type[node_type].fields,
262
)263
)264
)265
266
def has_text_element(self) -> bool:
267
for element_ in self.elements:
268
if element_.tag == "TEXT":
269
return True
270
return False
271
272
def has_custom_elements(self) -> bool:
273
"""
274
Return True when the grammar defines element types beyond the275
built-in set {SECTION, TEXT, REQUIREMENT}.276
277
When the grammar has not yet been resolved (elements_by_type is278
empty but import_from_file is set), True is returned so that the279
Markdown writer can emit TYPE fields for round-trip safety.280
"""281
if self.is_default:
282
return False
283
_builtin = {"SECTION", "TEXT", "REQUIREMENT"}
284
if len(self.elements_by_type) == 0:
285
return self.import_from_file is not None
286
return any(t not in _builtin for t in self.elements_by_type)
287
288
def add_element_first(self, element: GrammarElement) -> None:
289
self.elements.insert(0, element)
290
self.elements_by_type[element.tag] = element
291
self.registered_elements.add(element.tag)
292
self.is_default = False
293
294
def update_element(
295
self, existing_element: GrammarElement, updated_element: GrammarElement
296
) -> None:
297
element_index = self.elements.index(existing_element)
298
self.elements[element_index] = updated_element
299
self.elements_by_type[updated_element.tag] = updated_element
300
self.is_default = False
301
302
def update_with_elements(self, elements: List[GrammarElement]) -> None:
303
# When elements are created by code, not by textX, it is convenient304
# if their .parent is set here automatically.305
for element_ in elements:
306
element_.parent = self
307
308
registered_elements: Set[str] = set()
309
elements_by_type: Dict[str, GrammarElement] = {}
310
for element in elements:
311
registered_elements.add(element.tag)
312
elements_by_type[element.tag] = element
313
314
self.elements = elements
315
self.registered_elements = registered_elements
316
self.elements_by_type = elements_by_type
317
318
@staticmethod319
def create_default_text_element(
320
parent: Optional["DocumentGrammar"] = None,
321
enable_mid: bool = False,
322
) -> GrammarElement:
323
fields: List[GrammarElementFieldType] = []
324
if enable_mid:
325
fields.append(
326
GrammarElementFieldString(
327
parent=None,
328
title=RequirementFieldName.MID,
329
human_title=None,
330
required="False",
331
)332
)333
fields.extend(
334
[335
GrammarElementFieldString(
336
parent=None,
337
title=RequirementFieldName.UID,
338
human_title=None,
339
required="False",
340
),341
GrammarElementFieldString(
342
parent=None,
343
title=RequirementFieldName.STATEMENT,
344
human_title=None,
345
required="True",
346
),347
]348
)349
text_element = GrammarElement(
350
parent=parent,
351
tag="TEXT",
352
property_is_composite="",
353
property_prefix="",
354
property_view_style="",
355
fields=fields,
356
relations=[],
357
)358
return text_element
359
360
@staticmethod361
def create_default_section_element(
362
parent: Optional["DocumentGrammar"] = None, enable_mid: bool = False
363
) -> GrammarElement:
364
fields: List[GrammarElementFieldType] = []
365
if enable_mid:
366
fields.append(
367
GrammarElementFieldString(
368
parent=None,
369
title=RequirementFieldName.MID,
370
human_title=None,
371
required="False",
372
)373
)374
fields.append(
375
GrammarElementFieldString(
376
parent=None,
377
title=RequirementFieldName.UID,
378
human_title=None,
379
required="False",
380
)381
)382
fields.append(
383
GrammarElementFieldString(
384
parent=None,
385
title=RequirementFieldName.LEVEL,
386
human_title=None,
387
required="False",
388
)389
)390
fields.append(
391
GrammarElementFieldString(
392
parent=None,
393
title=RequirementFieldName.PREFIX,
394
human_title=None,
395
required="False",
396
)397
)398
fields.append(
399
GrammarElementFieldString(
400
parent=None,
401
title=RequirementFieldName.TITLE,
402
human_title=None,
403
required="True",
404
),405
)406
section_element = GrammarElement(
407
parent=parent,
408
tag="SECTION",
409
property_is_composite="True",
410
property_prefix="",
411
property_view_style="",
412
fields=fields,
413
relations=[],
414
)415
return section_element
416
417
418
class DocumentGrammarWrapper:
419
def __init__(self, grammar: DocumentGrammar):
420
assert isinstance(grammar, DocumentGrammar), grammar
421
self.grammar: DocumentGrammar = grammar