plain-parser 0.1.2.dev4__py3-none-any.whl → 0.1.2.dev5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plain_parser/concept_utils.py +2 -2
- plain_parser/loaders.py +19 -4
- plain_parser/plain_file.py +8 -6
- plain_parser/plain_spec.py +1 -1
- {plain_parser-0.1.2.dev4.dist-info → plain_parser-0.1.2.dev5.dist-info}/METADATA +1 -1
- plain_parser-0.1.2.dev5.dist-info/RECORD +13 -0
- plain_parser-0.1.2.dev4.dist-info/RECORD +0 -13
- {plain_parser-0.1.2.dev4.dist-info → plain_parser-0.1.2.dev5.dist-info}/WHEEL +0 -0
- {plain_parser-0.1.2.dev4.dist-info → plain_parser-0.1.2.dev5.dist-info}/entry_points.txt +0 -0
- {plain_parser-0.1.2.dev4.dist-info → plain_parser-0.1.2.dev5.dist-info}/licenses/LICENSE +0 -0
plain_parser/concept_utils.py
CHANGED
|
@@ -200,14 +200,14 @@ def sort_definitions(definitions: list[dict]) -> list[dict]:
|
|
|
200
200
|
concept_graph.add_node(node)
|
|
201
201
|
|
|
202
202
|
if not nx.is_directed_acyclic_graph(concept_graph):
|
|
203
|
-
msg = "Found cycles in the concept graph. Cycles are not allowed."
|
|
203
|
+
msg = "Found cycles in the concept graph. Cycles are not allowed. "
|
|
204
204
|
all_cycles = list(nx.simple_cycles(concept_graph))
|
|
205
205
|
|
|
206
206
|
for cycle in all_cycles:
|
|
207
207
|
cyclic_definitions = []
|
|
208
208
|
cyclic_definitions.append(concept_definitions[cycle[0]]["markdown"])
|
|
209
209
|
cyclic_definitions.append(concept_definitions[cycle[1]]["markdown"])
|
|
210
|
-
msg += "Cyclic
|
|
210
|
+
msg += "Cyclic definitions:\n"
|
|
211
211
|
msg += "\n".join(cyclic_definitions)
|
|
212
212
|
msg += "\n"
|
|
213
213
|
|
plain_parser/loaders.py
CHANGED
|
@@ -3,10 +3,10 @@ import re
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
5
|
from liquid2 import Environment, FileSystemLoader, StrictUndefined
|
|
6
|
-
from liquid2.exceptions import UndefinedError
|
|
6
|
+
from liquid2.exceptions import LiquidSyntaxError, UndefinedError
|
|
7
7
|
|
|
8
8
|
from plain_parser import plain_spec
|
|
9
|
-
from plain_parser.exceptions import UnsupportedBase64Content, UnsupportedResourceType
|
|
9
|
+
from plain_parser.exceptions import PlainSyntaxError, UnsupportedBase64Content, UnsupportedResourceType
|
|
10
10
|
from plain_parser.liquid_nodes import Plain2CodeIncludeTag, Plain2CodeLoaderMixin
|
|
11
11
|
|
|
12
12
|
MAX_BASE64_BLOB_LENGTH = 8192
|
|
@@ -95,7 +95,20 @@ class TrackingFileSystemLoader(Plain2CodeLoaderMixin, FileSystemLoader):
|
|
|
95
95
|
return source
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
def
|
|
98
|
+
def liquid_error_message(error: LiquidSyntaxError) -> str:
|
|
99
|
+
# Built from attributes: str() of some liquid2 syntax errors (e.g. an unterminated `{{ x`) raises ValueError.
|
|
100
|
+
template_name = error.template_name or None
|
|
101
|
+
token = error.token
|
|
102
|
+
if token is None or token.start < 0:
|
|
103
|
+
where = f" in {template_name}" if template_name else ""
|
|
104
|
+
else:
|
|
105
|
+
line = token.source[: token.start].count("\n") + 1
|
|
106
|
+
column = token.start - (token.source.rfind("\n", 0, token.start) + 1)
|
|
107
|
+
where = f" at {template_name}:{line}:{column}" if template_name else f" at {line}:{column}"
|
|
108
|
+
return f"Plain syntax error: Invalid Liquid{where}: {error.message}"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def get_loaded_templates(source_path, plain_source, template_name=None):
|
|
99
112
|
# Render the plain source with Liquid templating engine
|
|
100
113
|
# to identify the templates that are being loaded
|
|
101
114
|
|
|
@@ -106,9 +119,11 @@ def get_loaded_templates(source_path, plain_source):
|
|
|
106
119
|
liquid_env.filters["code_variable"] = plain_spec.code_variable_liquid_filter
|
|
107
120
|
liquid_env.filters["prohibited_chars"] = plain_spec.prohibited_chars_liquid_filter
|
|
108
121
|
|
|
109
|
-
plain_source_template = liquid_env.from_string(plain_source)
|
|
110
122
|
try:
|
|
123
|
+
plain_source_template = liquid_env.from_string(plain_source, name=template_name or "")
|
|
111
124
|
plain_source = plain_source_template.render()
|
|
125
|
+
except LiquidSyntaxError as e:
|
|
126
|
+
raise PlainSyntaxError(liquid_error_message(e))
|
|
112
127
|
except UndefinedError as e:
|
|
113
128
|
raise Exception(f"Undefined liquid variable: {str(e)}")
|
|
114
129
|
|
plain_parser/plain_file.py
CHANGED
|
@@ -240,7 +240,7 @@ def _is_acceptance_test_heading(token) -> tuple[bool, str | None]:
|
|
|
240
240
|
content = strong_children[0].content.strip()
|
|
241
241
|
if content == plain_spec.ACCEPTANCE_TEST_HEADING:
|
|
242
242
|
return True, None
|
|
243
|
-
problem = f"Syntax error at line {token.line_number}: Invalid acceptance test heading (`{content}`). Expected: `{plain_spec.ACCEPTANCE_TEST_HEADING}
|
|
243
|
+
problem = f"Syntax error at line {token.line_number}: Invalid acceptance test heading (`{content}`). Expected: `{plain_spec.ACCEPTANCE_TEST_HEADING}`."
|
|
244
244
|
return False, problem
|
|
245
245
|
|
|
246
246
|
|
|
@@ -508,6 +508,7 @@ def parse_plain_source( # noqa: C901
|
|
|
508
508
|
template_dirs: list[str],
|
|
509
509
|
imported_modules: list[str],
|
|
510
510
|
modules_trace: list[str],
|
|
511
|
+
module_name: str | None = None,
|
|
511
512
|
) -> PlainFileParseResult:
|
|
512
513
|
plain_source_text = normalize_line_endings(plain_source_text)
|
|
513
514
|
|
|
@@ -527,7 +528,7 @@ def parse_plain_source( # noqa: C901
|
|
|
527
528
|
else:
|
|
528
529
|
required_concepts = list[str]()
|
|
529
530
|
|
|
530
|
-
[_, loaded_templates] = loaders.get_loaded_templates(template_dirs, plain_source_text)
|
|
531
|
+
[_, loaded_templates] = loaders.get_loaded_templates(template_dirs, plain_source_text, module_name)
|
|
531
532
|
|
|
532
533
|
plain_source_content = restore_stripped_lines(plain_source_text, plain_source_obj.content)
|
|
533
534
|
|
|
@@ -585,7 +586,7 @@ def parse_plain_source( # noqa: C901
|
|
|
585
586
|
|
|
586
587
|
if specification_heading == plain_spec.DEFINITIONS and current_specification_heading is not None:
|
|
587
588
|
raise PlainSyntaxError(
|
|
588
|
-
f"Plain syntax error: Syntax error at line {token.line_number}: Definitions specification must be the first specification in the section
|
|
589
|
+
f"Plain syntax error: Syntax error at line {token.line_number}: Definitions specification must be the first specification in the section (`{token_text}`)"
|
|
589
590
|
)
|
|
590
591
|
|
|
591
592
|
current_specification_heading = specification_heading
|
|
@@ -602,7 +603,7 @@ def parse_plain_source( # noqa: C901
|
|
|
602
603
|
|
|
603
604
|
else:
|
|
604
605
|
raise PlainSyntaxError(
|
|
605
|
-
f"Plain syntax error: Syntax error at line {token.line_number}: Invalid source structure
|
|
606
|
+
f"Plain syntax error: Syntax error at line {token.line_number}: Invalid source structure (`{token_text}`)"
|
|
606
607
|
)
|
|
607
608
|
|
|
608
609
|
# Acceptance tests are only allowed nested under functional specs. Reject them when
|
|
@@ -681,8 +682,8 @@ def read_module_plain_source(module_name: str, template_dirs: list[str]) -> str:
|
|
|
681
682
|
if blob is not None:
|
|
682
683
|
raise UnsupportedBase64Content(
|
|
683
684
|
f"Module '{module_name}' contains a base64-encoded blob ({len(blob)} characters) "
|
|
684
|
-
"inlined in the specification. This is not supported."
|
|
685
|
-
"Remove the base64 data from the .plain file or if necessary,"
|
|
685
|
+
"inlined in the specification. This is not supported. "
|
|
686
|
+
"Remove the base64 data from the .plain file or if necessary, "
|
|
686
687
|
"include the binary file path in the specification."
|
|
687
688
|
)
|
|
688
689
|
|
|
@@ -704,6 +705,7 @@ def parse_plain_file(
|
|
|
704
705
|
template_dirs,
|
|
705
706
|
imported_modules,
|
|
706
707
|
modules_trace + [module_name],
|
|
708
|
+
module_name=module_name,
|
|
707
709
|
)
|
|
708
710
|
|
|
709
711
|
|
plain_parser/plain_spec.py
CHANGED
|
@@ -126,7 +126,7 @@ def collect_linked_resources(
|
|
|
126
126
|
):
|
|
127
127
|
|
|
128
128
|
if not isinstance(plain_source_tree, dict):
|
|
129
|
-
raise ValueError("
|
|
129
|
+
raise ValueError("plain_source_tree must be a dictionary.")
|
|
130
130
|
|
|
131
131
|
if frid is not None:
|
|
132
132
|
functional_requirements = get_frids(plain_source_tree)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
plain_parser/__init__.py,sha256=RzRI_EVcrPnPT_BzoYtrw09-HUzKGrW4HT-GTd8GrTU,725
|
|
2
|
+
plain_parser/cli.py,sha256=oJL_23l_K3ZWpJLRROGjFo2UOsJv7uUoP0RbfL8oit4,4682
|
|
3
|
+
plain_parser/concept_utils.py,sha256=cgbgJANz5j4cBXbOxH8bdIClkcClTNbP1wbvhcm6WUM,8102
|
|
4
|
+
plain_parser/exceptions.py,sha256=WcHK5g9K6bsE-ZZBKWl0lZtiMTQ6fif8-zdEx9qjmRw,795
|
|
5
|
+
plain_parser/liquid_nodes.py,sha256=CYmujdPJMK9hLjfFPmnSoKL3Ps4sb-2OOhH-fZUssis,3917
|
|
6
|
+
plain_parser/loaders.py,sha256=o-I7u1NL8hZdTWztqwO7zw072aGxXTBBYSAFDBRG4q0,5136
|
|
7
|
+
plain_parser/plain_file.py,sha256=NnPB9-xp2BIwJIaKmZnjTVjYCZPhlh4RLakR1CV6tUU,39867
|
|
8
|
+
plain_parser/plain_spec.py,sha256=Z79Vi8bdRiOiM53QyWS8OLNSlGPrQObSZx11Eh8i8Mk,15308
|
|
9
|
+
plain_parser-0.1.2.dev5.dist-info/METADATA,sha256=48Pi2W98iN5Om2JxQT-N_weUPWc0DYaEy6VgYycf8cA,2681
|
|
10
|
+
plain_parser-0.1.2.dev5.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
11
|
+
plain_parser-0.1.2.dev5.dist-info/entry_points.txt,sha256=pA31GGUyYrmZSpH1CuSzhjrv8pX5ac5HI5hFop1B4eI,55
|
|
12
|
+
plain_parser-0.1.2.dev5.dist-info/licenses/LICENSE,sha256=QClWKncxgRjNUVOW8Axi7zK9HtsMdJEtUSMA4LEe8fo,1071
|
|
13
|
+
plain_parser-0.1.2.dev5.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
plain_parser/__init__.py,sha256=RzRI_EVcrPnPT_BzoYtrw09-HUzKGrW4HT-GTd8GrTU,725
|
|
2
|
-
plain_parser/cli.py,sha256=oJL_23l_K3ZWpJLRROGjFo2UOsJv7uUoP0RbfL8oit4,4682
|
|
3
|
-
plain_parser/concept_utils.py,sha256=BJzddKxrwg2KbV-58R74ihIJKcv-CGA4B5NZyhOixQ8,8100
|
|
4
|
-
plain_parser/exceptions.py,sha256=WcHK5g9K6bsE-ZZBKWl0lZtiMTQ6fif8-zdEx9qjmRw,795
|
|
5
|
-
plain_parser/liquid_nodes.py,sha256=CYmujdPJMK9hLjfFPmnSoKL3Ps4sb-2OOhH-fZUssis,3917
|
|
6
|
-
plain_parser/loaders.py,sha256=eQbQ3bUzcTxj2FeKbNd8nAtInWwiLYbVbRSYkvebLxw,4286
|
|
7
|
-
plain_parser/plain_file.py,sha256=wI6NFbEQ5VeUKtqVv0rIbHNHdN2skQeaViVDalRONwc,39785
|
|
8
|
-
plain_parser/plain_spec.py,sha256=31mLXDldi9zOXT9aK3QhD2mbxbph4ArFU_qgWYd2nwc,15309
|
|
9
|
-
plain_parser-0.1.2.dev4.dist-info/METADATA,sha256=eQwOQvOgxL5suXwe95ZU309v06PgOhJhGibCQUng5x0,2681
|
|
10
|
-
plain_parser-0.1.2.dev4.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
11
|
-
plain_parser-0.1.2.dev4.dist-info/entry_points.txt,sha256=pA31GGUyYrmZSpH1CuSzhjrv8pX5ac5HI5hFop1B4eI,55
|
|
12
|
-
plain_parser-0.1.2.dev4.dist-info/licenses/LICENSE,sha256=QClWKncxgRjNUVOW8Axi7zK9HtsMdJEtUSMA4LEe8fo,1071
|
|
13
|
-
plain_parser-0.1.2.dev4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|