Skip to content

Commit 0e7c0b4

Browse files
committed
backend/sdoc: allows slashes in the node UID and MultipleChoice fields
Closes #2197
1 parent 9f7daac commit 0e7c0b4

15 files changed

+136
-78
lines changed

strictdoc/backend/sdoc/grammar/grammar.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
REGEX_UID = r"([\w]+[\w()\-. ]*)"
1+
REGEX_UID = r"([\w]+[\w()\-\/. ]*)"
22

33
NEGATIVE_MULTILINE_STRING_START = "(?!>>>\n)"
44
NEGATIVE_MULTILINE_STRING_END = "(?!^<<<)"
@@ -44,7 +44,7 @@
4444
;
4545
4646
FieldName[noskipws]:
47-
/{NEGATIVE_UID}{NEGATIVE_RELATIONS}[A-Z]+[A-Z_]*/
47+
/{NEGATIVE_UID}{NEGATIVE_RELATIONS}[A-Z]+[A-Z_0-9]*/
4848
;
4949
"""
5050

strictdoc/backend/sdoc/validations/sdoc_validator.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# mypy: disable-error-code="arg-type,no-redef,no-untyped-call,no-untyped-def,union-attr"
1+
# mypy: disable-error-code="arg-type,no-untyped-call,no-untyped-def,union-attr"
22
import re
33
from typing import Iterator, Optional, Set
44

@@ -19,11 +19,20 @@
1919
RequirementFieldName,
2020
)
2121

22+
MULTIPLE_CHOICE_FIELD_KW = r"[a-zA-Z0-9\/_]+"
23+
MULTIPLE_CHOICE_FIELD_REGEX = re.compile(
24+
rf"^{MULTIPLE_CHOICE_FIELD_KW}(, {MULTIPLE_CHOICE_FIELD_KW})*$"
25+
)
26+
27+
28+
def multi_choice_regex_match(value: str) -> bool:
29+
"""
30+
Validate MultipleChoice field value.
31+
Bug: Forward slashes in field values cause errors #2197,
32+
https://github.com/strictdoc-project/strictdoc/issues/2197
33+
"""
2234

23-
def multi_choice_regex_match(value):
24-
keyword = "[a-zA-Z0-9_]+"
25-
regex = re.compile(rf"^{keyword}(, {keyword})*$")
26-
match = regex.match(value)
35+
match = MULTIPLE_CHOICE_FIELD_REGEX.match(value)
2736
return match is not None
2837

2938

@@ -37,7 +46,7 @@ class as well. This would remove the need in the ParseContext class
3746
"""
3847

3948
@staticmethod
40-
def validate_document(document: SDocDocument):
49+
def validate_document(document: SDocDocument) -> None:
4150
assert isinstance(document, SDocDocument), document
4251
SDocValidator._validate_document_config(document)
4352
SDocValidator._validate_document_view(document)
@@ -46,23 +55,25 @@ def validate_document(document: SDocDocument):
4655
@staticmethod
4756
def validate_grammar_from_file(
4857
path_to_grammar: str, grammar_from_file: DocumentGrammar
49-
):
58+
) -> None:
5059
for grammar_element_ in grammar_from_file.elements:
5160
SDocValidator.validate_grammar_element(
5261
path_to_grammar, grammar_element_
5362
)
5463

5564
@staticmethod
56-
def _validate_grammar(document: SDocDocument):
65+
def _validate_grammar(document: SDocDocument) -> None:
66+
assert document.meta is not None
67+
assert document.grammar is not None
5768
for grammar_element_ in document.grammar.elements:
5869
SDocValidator.validate_grammar_element(
5970
document.meta.input_doc_full_path, grammar_element_
6071
)
6172

6273
@staticmethod
6374
def validate_grammar_element(
64-
path_to_grammar, grammar_element: GrammarElement
65-
):
75+
path_to_grammar: str, grammar_element: GrammarElement
76+
) -> None:
6677
if grammar_element.content_field[0] not in grammar_element.fields_map:
6778
raise StrictDocSemanticError.grammar_missing_reserved_statement(
6879
grammar_element,
@@ -101,7 +112,7 @@ def validate_grammar_element(
101112
)
102113

103114
@staticmethod
104-
def _validate_document_config(document: SDocDocument):
115+
def _validate_document_config(document: SDocDocument) -> None:
105116
document_config: DocumentConfig = document.config
106117
if document_config.default_view is not None:
107118
if document.view is None:
@@ -122,7 +133,7 @@ def _validate_document_config(document: SDocDocument):
122133
)
123134

124135
@staticmethod
125-
def _validate_document_view(document: SDocDocument):
136+
def _validate_document_view(document: SDocDocument) -> None:
126137
document_view: Optional[DocumentView] = document.view
127138
if document_view is not None:
128139
for view in document_view.views:
@@ -156,14 +167,14 @@ def validate_node(
156167
requirement: SDocNode,
157168
document_grammar: DocumentGrammar,
158169
path_to_sdoc_file: str,
159-
auto_uid_mode: bool = True,
160-
):
170+
auto_uid_mode: bool = False,
171+
) -> None:
161172
if requirement.node_type not in document_grammar.registered_elements:
162173
raise StrictDocSemanticError.unknown_node_type(
163174
requirement, path_to_sdoc_file
164175
)
165176

166-
grammar_element = document_grammar.elements_by_type[
177+
grammar_element: GrammarElement = document_grammar.elements_by_type[
167178
requirement.node_type
168179
]
169180
registered_fields: Set[str] = set(grammar_element.get_field_titles())
@@ -177,9 +188,6 @@ def validate_node(
177188
path_to_sdoc_file=path_to_sdoc_file,
178189
)
179190

180-
grammar_element: GrammarElement = document_grammar.elements_by_type[
181-
requirement.node_type
182-
]
183191
grammar_fields_iterator: Iterator[GrammarElementField] = iter(
184192
grammar_element.fields
185193
)
@@ -250,6 +258,9 @@ def validate_requirement_field(
250258
path_to_sdoc_file: str,
251259
auto_uid_mode: bool = True,
252260
) -> bool:
261+
"""
262+
Validate a single node field using its grammar element field definition.
263+
"""
253264
if grammar_field is None:
254265
if requirement_field is None:
255266
# Both grammar and requirements fields are over.
@@ -282,11 +293,6 @@ def validate_requirement_field(
282293
grammar_field.title
283294
not in requirement.ordered_fields_lookup.keys()
284295
):
285-
# A special case: The Manage UID command auto-generates the UID,
286-
# so the field presence validation has to be relaxed.
287-
# The GitHub issue report:
288-
# manage auto-uid: UID field REQUIRED True leads to an error
289-
# https://github.com/strictdoc-project/strictdoc/issues/1896
290296
if grammar_field.title == "UID" and auto_uid_mode:
291297
return False
292298

strictdoc/cli/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ def _main(parallelizer: Parallelizer) -> None:
129129
# FIXME: This must be improved.
130130
project_config.input_paths = [manage_config.input_path]
131131
# FIXME: This must be improved.
132+
project_config.auto_uid_mode = True
132133
project_config.autouuid_include_sections = (
133134
manage_config.include_sections
134135
)

strictdoc/commands/manage_autouid_command.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ def execute(*, project_config: ProjectConfig, parallelizer: Parallelizer):
2828
TraceabilityIndexBuilder.create(
2929
project_config=project_config,
3030
parallelizer=parallelizer,
31-
auto_uid_mode=True,
3231
)
3332
)
3433
except DocumentTreeError as exc:

strictdoc/core/document_meta.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ class DocumentMeta:
1212
def __init__(
1313
self,
1414
level: int,
15-
file_tree_mount_folder,
15+
file_tree_mount_folder: str,
1616
document_filename: str,
17-
document_filename_base,
18-
input_doc_full_path,
17+
document_filename_base: str,
18+
input_doc_full_path: str,
1919
input_doc_rel_path: SDocRelativePath,
2020
input_doc_dir_rel_path: SDocRelativePath,
2121
input_doc_assets_dir_rel_path: SDocRelativePath,
2222
output_document_dir_full_path: str,
2323
output_document_dir_rel_path: SDocRelativePath,
24-
):
24+
) -> None:
2525
"""
2626
Example explaining meta data stored by this class:
2727

strictdoc/core/project_config.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,18 @@ def __init__(
173173
self.reqif_enable_mid: bool = reqif_enable_mid
174174
self.reqif_import_markup: Optional[str] = reqif_import_markup
175175

176+
#
177+
# auto_uid_mode: default is False. The True-case is used by the
178+
# manage/auto_uid command: the SDocNodeValidator will
179+
# not raise an exception if it sees a node with a missing UID.
180+
# Important for a special case:
181+
# The Manage UID command auto-generates the UID, so the field presence
182+
# validation has to be relaxed.
183+
# The GitHub issue report:
184+
# manage auto-uid: UID field REQUIRED True leads to an error
185+
# https://github.com/strictdoc-project/strictdoc/issues/1896
186+
#
187+
self.auto_uid_mode = False
176188
self.autouuid_include_sections: bool = False
177189

178190
self.config_last_update: Optional[datetime.datetime] = (

strictdoc/core/traceability_index_builder.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ def create(
6666
project_config: ProjectConfig,
6767
parallelizer,
6868
skip_source_files: bool = False,
69-
auto_uid_mode: bool = False,
7069
) -> TraceabilityIndex:
7170
# TODO: It would be great to hide this code behind --development flag.
7271
# There is no need for this to be activated in the Pip-released builds.
@@ -109,7 +108,6 @@ def create(
109108
TraceabilityIndexBuilder.create_from_document_tree(
110109
document_tree,
111110
project_config,
112-
auto_uid_mode,
113111
)
114112
)
115113
traceability_index.document_tree = document_tree
@@ -208,7 +206,6 @@ def create(
208206
def create_from_document_tree(
209207
document_tree: DocumentTree,
210208
project_config: ProjectConfig,
211-
auto_uid_mode: bool = True,
212209
) -> TraceabilityIndex:
213210
# FIXME: Too many things going on below. Would be great to simplify this
214211
# workflow.
@@ -386,7 +383,7 @@ def create_from_document_tree(
386383
node,
387384
document_grammar=document.grammar,
388385
path_to_sdoc_file=document.meta.input_doc_full_path,
389-
auto_uid_mode=auto_uid_mode,
386+
auto_uid_mode=project_config.auto_uid_mode,
390387
)
391388
except StrictDocSemanticError as exc:
392389
print(exc.to_print_message()) # noqa: T201

strictdoc/export/spdx/spdx_generator.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,8 @@ def export_tree(
235235
lookup_file_name_to_spdx_file: Dict[str, File] = {}
236236

237237
for document_ in traceability_index.document_tree.document_list:
238-
with open(document_.meta.input_doc_full_path, "rb") as file_:
239-
document_bytes = file_.read()
238+
with open(document_.meta.input_doc_full_path, "rb") as input_file_:
239+
document_bytes = input_file_.read()
240240

241241
"""
242242
Create SPDX File from SDoc Document.
@@ -318,8 +318,8 @@ def export_tree(
318318
if node_link_path_ in lookup_file_name_to_spdx_file:
319319
continue
320320

321-
with open(node_link_path_, "rb") as file_:
322-
file_bytes = file_.read()
321+
with open(node_link_path_, "rb") as node_link_file_:
322+
file_bytes = node_link_file_.read()
323323

324324
source_spdx_file = (
325325
sdoc_spdx_converter.convert_file_to_file(
@@ -435,10 +435,10 @@ def export_tree(
435435
if True:
436436
sdoc_document = SPDXToSDocConverter.convert(spdx_container)
437437

438-
sdoc_output = SDWriter(project_config).write(sdoc_document)
438+
sdoc_output: str = SDWriter(project_config).write(sdoc_document)
439439

440440
sdoc_output_path = os.path.join(
441441
output_spdx_root, "output.spdx.sdoc"
442442
)
443-
with open(sdoc_output_path, "w", encoding="utf8") as file_:
444-
file_.write(sdoc_output)
443+
with open(sdoc_output_path, "w", encoding="utf8") as output_file_:
444+
output_file_.write(sdoc_output)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from strictdoc.core.document_meta import DocumentMeta
2+
from strictdoc.helpers.paths import SDocRelativePath
3+
4+
5+
def create_fake_document_meta():
6+
return DocumentMeta(
7+
level=1,
8+
file_tree_mount_folder="root_folder",
9+
document_filename="fake_doc.sdoc",
10+
document_filename_base="fake_doc",
11+
input_doc_full_path="/tmp/root_folder/fake_doc.sdoc",
12+
input_doc_rel_path=SDocRelativePath("fake_doc.sdoc"),
13+
input_doc_dir_rel_path=SDocRelativePath(""),
14+
input_doc_assets_dir_rel_path=SDocRelativePath("_assets"),
15+
output_document_dir_full_path="/tmp/output/root_folder/",
16+
output_document_dir_rel_path=SDocRelativePath(""),
17+
)

0 commit comments

Comments
 (0)