Skip to content

Commit b54a238

Browse files
committed
docs: document the migration path: [SECTION] -> [[SECTION]]
1 parent 3375caf commit b54a238

File tree

16 files changed

+415
-21
lines changed

16 files changed

+415
-21
lines changed

docs/strictdoc_01_user_guide.sdoc

Lines changed: 129 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ ELEMENTS:
1414
- TAG: SECTION
1515
PROPERTIES:
1616
IS_COMPOSITE: True
17+
PREFIX: None
1718
VIEW_STYLE: Narrative
1819
FIELDS:
1920
- TITLE: MID
@@ -544,15 +545,40 @@ TITLE: Document structure
544545
[TEXT]
545546
MID: 5419c0c9bfcc4922ac5aa79ddb333188
546547
STATEMENT: >>>
547-
An SDoc document consists of a ``[DOCUMENT]`` declaration followed by a sequence of nodes:
548+
An SDoc document consists of a ``[DOCUMENT]`` declaration followed by a sequence of **leaf nodes** or **composite nodes**.
548549

549-
- Lead nodes: ``[TEXT]`` or ``[REQUIREMENT]``
550-
- Composite nodes: ``[COMPOSITE_REQUIREMENT]``
551-
- Section nodes that group other nodes recursively: ``[SECTION]``.
550+
A **leaf node** cannot contain any child nodes. It is simply a node with text fields. The default leaf nodes are defined as ``[TEXT]`` or ``[REQUIREMENT]``. Any other ``[...]`` custom leaf node can be registered by a user in a document grammar.
552551

553-
Each construct is described in more detail below.
552+
Example of a lead node:
553+
554+
.. code-block:: text
555+
556+
[REQUIREMENT]
557+
STATEMENT: StrictDoc shall be based on a document model.
558+
559+
A **composite node** can contain other composite or leaf nodes. The default composite node is ``[[SECTION]]`` which models a section/chapter in a document. Any other ``[[...]]`` composite node can be registered by a user in a document grammar.
560+
561+
Example of a composite node:
562+
563+
.. code-block:: text
564+
565+
[[SECTION]]
566+
TITLE: Document model
567+
568+
[REQUIREMENT]
569+
STATEMENT: StrictDoc shall be based on a document model.
570+
571+
[[/SECTION]]
572+
573+
The document elements are described below.
554574
<<<
555575

576+
[[/SECTION]]
577+
578+
[[SECTION]]
579+
MID: ec9dcd4ad28a4852bc47723e667006df
580+
TITLE: Syntax rules
581+
556582
[[SECTION]]
557583
MID: 926894ac8efc4e99943741691b3d4efe
558584
UID: SECTION-UG-Strict-rule-1
@@ -561,7 +587,7 @@ TITLE: Strict rule #1: One empty line between all nodes
561587
[TEXT]
562588
MID: 2ebd1f2d3c8746a383d71232756c3bd3
563589
STATEMENT: >>>
564-
StrictDoc's grammar requires each node, such as ``[REQUIREMENT]``, ``[SECTION]``,
590+
StrictDoc's grammar requires each node, such as ``[REQUIREMENT]``, ``[[SECTION]]``,
565591
etc., to be separated with exactly one empty line from the nodes surrounding it.
566592
This rule is valid for all nodes. Absence of an empty line or presence of more
567593
than one empty line between two nodes will result in an SDoc parsing error.
@@ -577,11 +603,13 @@ TITLE: Strict rule #2: No content is allowed outside of SDoc grammar
577603
[TEXT]
578604
MID: 686eb01cf7db44c4bd1edd72ccb02ed0
579605
STATEMENT: >>>
580-
StrictDoc's grammar does not allow any content to be written outside of the SDoc
581-
grammatical constructs. It is assumed that the critical content shall always be
582-
written in form of requirements:
583-
``[REQUIREMENT]`` and ``[COMPOSITE_REQUIREMENT]``. Non-critical content shall
584-
be specified using ``[TEXT]`` nodes.
606+
StrictDoc's grammar does not allow any content to be written outside the SDoc grammatical constructs.
607+
608+
It is assumed that the critical content shall always be
609+
written in form of leaf nodes and composite nodes, such as
610+
``[REQUIREMENT]`` and ``[[SECTION]]``.
611+
612+
Non-critical content shall be specified using the ``[TEXT]`` leaf nodes.
585613
<<<
586614

587615
[[/SECTION]]
@@ -1228,7 +1256,11 @@ TITLE: Section
12281256
[TEXT]
12291257
MID: 14e5fea31b1c41bf8c743f18e51053e0
12301258
STATEMENT: >>>
1231-
The ``[SECTION]`` element is used for creating document chapters and grouping
1259+
.. warning::
1260+
1261+
As of 2025 Q2, StrictDoc is switching to a new ``[[SECTION]]`` syntax for declaring sections (previously ``[SECTION]``). If you are an existing user, please follow the migration guide [LINK: SECTION-UG-NODE-MIGRATION].
1262+
1263+
The ``[[SECTION]]`` element can used for creating document chapters and grouping
12321264
requirements into logical groups. It is equivalent to the use of ``#``, ``##``,
12331265
``###``, etc., in Markdown and ``====``, ``----``, ``~~~~`` in RST.
12341266

@@ -3849,6 +3881,91 @@ The existing workaround for this problem is to increase a number of semaphores i
38493881
MID: b55c23b113d54802b5717f7a82a45bd2
38503882
TITLE: Appendices
38513883

3884+
[[SECTION]]
3885+
MID: 2ed20461aeb4462b812432c08aa006be
3886+
UID: SECTION-UG-NODE-MIGRATION
3887+
TITLE: [[NODE]] migration
3888+
3889+
[TEXT]
3890+
MID: 9d55b69bf789487e814210da06fc4164
3891+
STATEMENT: >>>
3892+
As of 2025 Q2, the following changes are made to StrictDoc's grammar in a partially backward-incompatible manner. The relevant GitHub issue related to the migration:
3893+
3894+
`Migration: SDoc model: Merge Section, Node, Composite Node into just Node #2193 <https://github.com/strictdoc-project/strictdoc/issues/2193>`_
3895+
3896+
1\) The ``[[CUSTOM_NODE]]`` syntax with two square brackets around the element name has been introduced for composite nodes.
3897+
3898+
Example of declaring a composite node in a grammar and using it in a document:
3899+
3900+
.. code-block::
3901+
3902+
[GRAMMAR]
3903+
ELEMENTS:
3904+
- TAG: SECTION
3905+
PROPERTIES:
3906+
IS_COMPOSITE: True
3907+
PREFIX: None
3908+
VIEW_STYLE: Narrative
3909+
FIELDS:
3910+
- TITLE: MID
3911+
TYPE: String
3912+
REQUIRED: False
3913+
- TITLE: UID
3914+
TYPE: String
3915+
REQUIRED: False
3916+
- TITLE: TITLE
3917+
TYPE: String
3918+
REQUIRED: True
3919+
3920+
[[SECTION]]
3921+
TITLE: Section #1
3922+
3923+
[[/SECTION]]
3924+
3925+
Using the new syntax, it is now possible to declare any custom composite element, such as what ``[COMPOSITE_REQUIREMENT]`` used to provide before.
3926+
3927+
2\) The previously used ``[SECTION]`` syntax with one square bracket around the SECTION is now DEPRECATED, but the old behavior is still supported. The users are encouraged to migrate all ``[SECTION]`` elements to the new ``[[SECTION]]`` syntax. To support the new behavior, the grammar must include a declaration for a composite SECTION element similar to the example provided above.
3928+
3929+
3\) The ``[COMPOSITE_REQUIREMENT]`` node has been removed from the codebase. This is a backward-incompatible change, but it is assumed that no users should be affected since this feature has never been developed enough to be useful.
3930+
<<<
3931+
3932+
[[SECTION]]
3933+
MID: 6f15ef6876ba48598d5560aaf4664ec9
3934+
TITLE: How to migrate from [SECTION] to [[SECTION]]
3935+
3936+
[TEXT]
3937+
MID: 19fe5099aa034eddb960cc88953ace0e
3938+
STATEMENT: >>>
3939+
The migration from ``[SECTION]`` to the new composite ``[[SECTION]]`` syntax is mostly straightforward and can be done manually. However, StrictDoc also provides a dedicated configuration option to assist with this migration.
3940+
3941+
1\) **Manual approach**
3942+
3943+
Update all instances of ``[SECTION]`` to ``[[SECTION]]`` manually, or use a combination of ``find`` and ``sed``. Additionally, make sure to update all document grammar declarations to include the new composite ``[[SECTION]]`` element.
3944+
3945+
2\) **Semi-automated approach (experimental)**
3946+
3947+
StrictDoc can automatically migrate SDoc files if the project configuration option ``section_behavior`` is set as follows:
3948+
3949+
.. code-block::
3950+
3951+
[project]
3952+
section_behavior = "[[SECTION]]"
3953+
3954+
With this option enabled, StrictDoc's ``export`` command will convert all ``[SECTION]`` nodes to ``[[SECTION]]`` when writing SDoc files.
3955+
3956+
To perform the migration, run the export command:
3957+
3958+
.. code-block::
3959+
3960+
strictdoc export . --formats=sdoc
3961+
3962+
The resulting files will have all ``[SECTION]`` nodes replaced with ``[[SECTION]]``.
3963+
<<<
3964+
3965+
[[/SECTION]]
3966+
3967+
[[/SECTION]]
3968+
38523969
[[SECTION]]
38533970
MID: a72ae310493a42ba8ecdc7de2c42d4f2
38543971
UID: SECTION-UG-FREETEXT-TEXT

strictdoc/backend/sdoc/models/document_grammar.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,22 @@ def create_default_section_element(
581581
required="False",
582582
)
583583
)
584+
fields.append(
585+
GrammarElementFieldString(
586+
parent=None,
587+
title=RequirementFieldName.LEVEL,
588+
human_title=None,
589+
required="False",
590+
)
591+
)
592+
fields.append(
593+
GrammarElementFieldString(
594+
parent=None,
595+
title=RequirementFieldName.PREFIX,
596+
human_title=None,
597+
required="False",
598+
)
599+
)
584600
fields.append(
585601
GrammarElementFieldString(
586602
parent=None,

strictdoc/backend/sdoc/processor.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424

2525

2626
class ParseContext:
27-
def __init__(self, path_to_sdoc_file: Optional[str]):
27+
def __init__(
28+
self, path_to_sdoc_file: Optional[str], migrate_sections: bool = False
29+
):
2830
self.path_to_sdoc_file: Optional[str] = path_to_sdoc_file
2931
self.path_to_sdoc_dir: Optional[str] = None
3032
if path_to_sdoc_file is not None:
@@ -37,6 +39,7 @@ def __init__(self, path_to_sdoc_file: Optional[str]):
3739
self.document_has_requirements = False
3840

3941
self.fragments_from_files: List[SDocDocumentFromFileIF] = []
42+
self.migrate_sections: bool = migrate_sections
4043

4144

4245
class SDocParsingProcessor:
@@ -46,7 +49,10 @@ def __init__(self, parse_context: ParseContext):
4649
def process_document(self, document: SDocDocument):
4750
document.grammar = (
4851
self.parse_context.document_grammar
49-
or DocumentGrammar.create_default(document)
52+
or DocumentGrammar.create_default(
53+
document,
54+
create_section_element=self.parse_context.migrate_sections,
55+
)
5056
)
5157
self.parse_context.document = document
5258
document.ng_including_document_reference = (

strictdoc/backend/sdoc/reader.py

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ class SDReader:
2929
)
3030

3131
@staticmethod
32-
def _read(input_string, file_path=None):
33-
parse_context = ParseContext(path_to_sdoc_file=file_path)
32+
def _read(input_string, file_path=None, migrate_sections: bool = False):
33+
parse_context = ParseContext(
34+
path_to_sdoc_file=file_path, migrate_sections=migrate_sections
35+
)
3436
processor = SDocParsingProcessor(parse_context=parse_context)
3537
SDReader.meta_model.register_obj_processors(
3638
processor.get_default_processors()
@@ -84,7 +86,9 @@ def read_from_file(
8486

8587
try:
8688
sdoc, parse_context = self.read_with_parse_context(
87-
sdoc_content, file_path=file_path
89+
sdoc_content,
90+
file_path=file_path,
91+
migrate_sections=project_config.is_new_section_behavior(),
8892
)
8993

9094
sdoc.fragments_from_files = parse_context.fragments_from_files
@@ -118,9 +122,6 @@ def read_from_file(
118122

119123
@staticmethod
120124
def convert(section: SDocSection) -> SDocNode:
121-
"""
122-
FIXME: Handle LEVEL, REQ_PREFIX
123-
"""
124125
fields = []
125126

126127
if section.mid_permanent:
@@ -141,6 +142,27 @@ def convert(section: SDocSection) -> SDocNode:
141142
multiline=False,
142143
)
143144
)
145+
if section.ng_resolved_custom_level is not None:
146+
fields.append(
147+
SDocNodeField.create_from_string(
148+
None,
149+
field_name="LEVEL",
150+
field_value=section.ng_resolved_custom_level,
151+
multiline=False,
152+
)
153+
)
154+
if (
155+
section.requirement_prefix is not None
156+
and len(section.requirement_prefix) > 0
157+
):
158+
fields.append(
159+
SDocNodeField.create_from_string(
160+
None,
161+
field_name="PREFIX",
162+
field_value=section.requirement_prefix,
163+
multiline=False,
164+
)
165+
)
144166
fields.append(
145167
SDocNodeField.create_from_string(
146168
None,
@@ -160,6 +182,14 @@ def convert(section: SDocSection) -> SDocNode:
160182
)
161183
for field_ in fields:
162184
field_.parent = node
185+
186+
node.ng_including_document_reference = (
187+
section.ng_including_document_reference
188+
)
189+
node.ng_document_reference = section.ng_document_reference
190+
node.ng_level = section.ng_level
191+
node.ng_resolved_custom_level = section.ng_resolved_custom_level
192+
163193
return node
164194

165195
@staticmethod

strictdoc/core/project_config.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class ProjectConfig:
7979
DEFAULT_SERVER_PORT = 5111
8080
DEFAULT_BUNDLE_DOCUMENT_VERSION = "@GIT_VERSION (Git branch: @GIT_BRANCH)"
8181
DEFAULT_BUNDLE_DOCUMENT_COMMIT_DATE = "@GIT_COMMIT_DATETIME"
82+
DEFAULT_SECTION_BEHAVIOR = "[SECTION]"
8283

8384
def __init__(
8485
self,
@@ -107,6 +108,7 @@ def __init__(
107108
reqif_import_markup: Optional[str],
108109
config_last_update: Optional[datetime.datetime],
109110
chromedriver: Optional[str],
111+
section_behavior: Optional[str],
110112
):
111113
assert isinstance(environment, SDocRuntimeEnvironment)
112114
if source_root_path is not None:
@@ -193,6 +195,7 @@ def __init__(
193195
self.is_running_on_server: bool = False
194196
self.view: Optional[str] = None
195197
self.chromedriver: Optional[str] = chromedriver
198+
self.section_behavior: Optional[str] = section_behavior
196199

197200
@staticmethod
198201
def default_config(environment: SDocRuntimeEnvironment) -> "ProjectConfig":
@@ -221,6 +224,7 @@ def default_config(environment: SDocRuntimeEnvironment) -> "ProjectConfig":
221224
reqif_import_markup=None,
222225
config_last_update=None,
223226
chromedriver=None,
227+
section_behavior=ProjectConfig.DEFAULT_SECTION_BEHAVIOR,
224228
)
225229

226230
# Some server command settings can override the project config settings.
@@ -347,6 +351,9 @@ def is_activated_source_file_language_parsers(self) -> bool:
347351
ProjectFeature.SOURCE_FILE_LANGUAGE_PARSERS in self.project_features
348352
)
349353

354+
def is_new_section_behavior(self):
355+
return self.section_behavior == "[[SECTION]]"
356+
350357
def get_strictdoc_root_path(self) -> str:
351358
return self.environment.path_to_strictdoc
352359

@@ -441,6 +448,8 @@ def _load_from_dictionary(
441448
reqif_import_markup: Optional[str] = None
442449
chromedriver: Optional[str] = None
443450

451+
section_behavior: str = ProjectConfig.DEFAULT_SECTION_BEHAVIOR
452+
444453
if "project" in config_dict:
445454
project_content = config_dict["project"]
446455
project_title = project_content.get("title", project_title)
@@ -610,6 +619,11 @@ def _load_from_dictionary(
610619
assert isinstance(test_report_root_entry_, dict)
611620
test_report_root_dict.update(test_report_root_entry_)
612621

622+
section_behavior = project_content.get(
623+
"section_behavior", section_behavior
624+
)
625+
assert section_behavior in ("[SECTION]", "[[SECTION]]")
626+
613627
if "server" in config_dict:
614628
server_content = config_dict["server"]
615629
server_host = server_content.get("host", server_host)
@@ -673,4 +687,5 @@ def _load_from_dictionary(
673687
reqif_import_markup=reqif_import_markup,
674688
config_last_update=config_last_update,
675689
chromedriver=chromedriver,
690+
section_behavior=section_behavior,
676691
)

tasks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def clean_itest_artifacts(context):
107107
run_invoke(
108108
context,
109109
"""
110-
git clean -dfX tests/integration/
110+
git clean -dX --force --quiet tests/integration/
111111
""",
112112
warn=True,
113113
)

0 commit comments

Comments
 (0)