Skip to content

Commit 88ebff0

Browse files
Refine 'no implementation reqs' error; hint that requires does not inherit them
1 parent 3a029b1 commit 88ebff0

2 files changed

Lines changed: 39 additions & 8 deletions

File tree

plain_file.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,19 +168,30 @@ def count_functionalities(plain_source) -> int:
168168
return len(section.children) if section is not None else 0
169169

170170

171-
def validate_functionalities_have_implementation_reqs(plain_source, module_name) -> None:
172-
"""Raise if the module has functionalities but no implementation reqs are specified."""
171+
def validate_functionalities_have_implementation_reqs(plain_source, module_name, has_requires=False) -> None:
172+
"""Raise if the module has functionalities but no implementation reqs are specified.
173+
174+
``has_requires`` indicates whether the module depends on another module via ``requires``.
175+
Unlike ``import``, ``requires`` does not inherit implementation reqs, so a hint is added to
176+
steer users who expected inheritance.
177+
"""
173178
implementation_reqs = plain_source[plain_spec.NON_FUNCTIONAL_REQUIREMENTS]
174179
has_implementation_reqs = (
175180
implementation_reqs is not None
176181
and hasattr(implementation_reqs, "children")
177182
and len(implementation_reqs.children) > 0
178183
)
179184
if not has_implementation_reqs:
180-
raise PlainSyntaxError(
181-
f"Plain syntax error: Module '{module_name}' specifies functionalities but no implementation reqs. "
182-
f"At least one implementation req is required."
185+
message = (
186+
f"Plain syntax error: Module '{module_name}' defines functionalities but specifies no "
187+
f"{plain_spec.NON_FUNCTIONAL_REQUIREMENTS}. At least one implementation req is required."
183188
)
189+
if has_requires:
190+
message += (
191+
" Implementation reqs are not inherited through 'requires' — "
192+
"add them to this module, or 'import' a module that provides them."
193+
)
194+
raise PlainSyntaxError(message)
184195

185196

186197
def _is_acceptance_test_heading(token) -> tuple[bool, str | None]:
@@ -763,7 +774,11 @@ def plain_file_parser( # noqa: C901
763774
f"'{plain_spec.FUNCTIONAL_REQUIREMENTS}' section. At least one functionality must be specified."
764775
)
765776

766-
validate_functionalities_have_implementation_reqs(plain_file_parse_result.plain_source, module_name)
777+
validate_functionalities_have_implementation_reqs(
778+
plain_file_parse_result.plain_source,
779+
module_name,
780+
has_requires=bool(plain_file_parse_result.required_modules),
781+
)
767782

768783
exported_definitions = process_required_modules(
769784
plain_file_parse_result.required_modules,

tests/test_plainfileparser.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def test_duplicate_specification_heading(get_test_data_path):
176176
def test_missing_non_functional_requirements(get_test_data_path):
177177
with pytest.raises(
178178
Exception,
179-
match="specifies functionalities but no implementation reqs",
179+
match="defines functionalities but specifies no implementation reqs",
180180
):
181181
plain_file.plain_file_parser(
182182
"missing_non_functional_requirements.plain",
@@ -187,14 +187,30 @@ def test_missing_non_functional_requirements(get_test_data_path):
187187
def test_without_non_functional_requirement(get_test_data_path):
188188
with pytest.raises(
189189
Exception,
190-
match="specifies functionalities but no implementation reqs",
190+
match="defines functionalities but specifies no implementation reqs",
191191
):
192192
plain_file.plain_file_parser(
193193
"without_non_functional_requirement.plain",
194194
[get_test_data_path("data/plainfile")],
195195
)
196196

197197

198+
def test_missing_impl_reqs_with_requires_adds_hint():
199+
plain_source = {plain_spec.NON_FUNCTIONAL_REQUIREMENTS: None}
200+
with pytest.raises(
201+
PlainSyntaxError,
202+
match="not inherited through 'requires'",
203+
):
204+
plain_file.validate_functionalities_have_implementation_reqs(plain_source, "my_app", has_requires=True)
205+
206+
207+
def test_missing_impl_reqs_without_requires_has_no_hint():
208+
plain_source = {plain_spec.NON_FUNCTIONAL_REQUIREMENTS: None}
209+
with pytest.raises(PlainSyntaxError) as exc_info:
210+
plain_file.validate_functionalities_have_implementation_reqs(plain_source, "my_app", has_requires=False)
211+
assert "requires" not in str(exc_info.value)
212+
213+
198214
def test_no_functional_specs_section(get_test_data_path):
199215
with pytest.raises(
200216
MissingFunctionalitiesError,

0 commit comments

Comments
 (0)