Skip to content

Use a match statement to dispatch on gherkin Child fields#822

Open
Pierre-Sassoulas wants to merge 4 commits into
masterfrom
refactor/match-gherkin-child
Open

Use a match statement to dispatch on gherkin Child fields#822
Pierre-Sassoulas wants to merge 4 commits into
masterfrom
refactor/match-gherkin-child

Conversation

@Pierre-Sassoulas

Copy link
Copy Markdown
Member

Opinionated change following #821. A gherkin Child has exactly one of background/rule/scenario set; class patterns make that dispatch explicit and replace the truthiness checks with type matching.

@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.00000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.06%. Comparing base (582f045) to head (b837fc9).

Files with missing lines Patch % Lines
src/pytest_bdd/parser.py 70.00% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #822      +/-   ##
==========================================
- Coverage   96.12%   96.06%   -0.06%     
==========================================
  Files          55       55              
  Lines        2423     2441      +18     
  Branches      136      136              
==========================================
+ Hits         2329     2345      +16     
- Misses         57       59       +2     
  Partials       37       37              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread src/pytest_bdd/parser.py Outdated
document.feature.children.append(Child())
return document

monkeypatch.setattr(parser_module, "get_gherkin_document", get_gherkin_document_with_unknown_child)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to mock? Not clear to me what's going on and why we need this, and how it relates to the change made

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's done in order to cover all the code branches to make codecov happy. With current gherkin code never produce a Child with none of background/rule/scenario set, so no real .feature file can exercise the fall-through branch. (I can drop it if you want)

Base automatically changed from chore/dep-cves-and-py3.15 to master July 5, 2026 18:06
Comment thread src/pytest_bdd/parser.py Outdated
@youtux

youtux commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

If we adopt the assert_never suggestion, we shouldn't need the test, as mypy would cover it

@Pierre-Sassoulas

Copy link
Copy Markdown
Member Author

mypy doesn't understand it (yet?) sadly. child.background or child.rule or child.scenario gives mypy Background | Rule | Scenario | None so I had to handle the None too.

Comment thread src/pytest_bdd/parser.py Outdated
# An empty Child (a child kind newer than this gherkin version);
# skip it, like unknown children were skipped before the match.
pass
case unreachable: # pragma: no cover

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although I wonder, if they add new types, maybe we should gracefully ignore them, but add a warning message? But our CI wouldn’t break so we wouldn’t really see it… but i think maybe it’s for the best, so that we have less maintenance burden.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think the warning message make sense. If it triggers it's going to be a lot of duplicate issue opened, but it's probably going to be duplicate issue in any case, just less obvious. Let me know if I should add it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'd say to remove the assert_never and instead have a catch-all case that emits a warning

A gherkin Child has exactly one of background/rule/scenario set; class
patterns make that dispatch explicit and replace the truthiness checks
with type matching.
A Child with none of background/rule/scenario set (a child kind a future
gherkin version could introduce) must be skipped by FeatureParser.parse.
No real feature file can produce one today, so the test appends a
synthetic empty Child to the parsed document.
…k test

assert_never on the Child dataclass itself does not typecheck: mypy cannot
prove exhaustiveness from attribute patterns on a class with three optional
fields, so the fall-through arm keeps the type "Child" instead of "Never".
Matching on `child.background or child.rule or child.scenario` gives mypy a
proper union (Background | Rule | Scenario | None) to exhaust; removing any
arm is now a mypy error.

Both defensive arms (empty Child, assert_never) are unreachable with the
current gherkin version, so they are excluded from coverage instead of being
exercised through a monkeypatched document, and the mock-based test is
removed. "pragma: no cover" is re-added to coverage's exclude_lines: setting
exclude_lines in the config replaces coverage.py's defaults, so the pragma
had silently stopped working.
@Pierre-Sassoulas Pierre-Sassoulas force-pushed the refactor/match-gherkin-child branch from d01b886 to 0bc1bdf Compare July 6, 2026 08:06
Per review feedback, replace the assert_never fall-through (which would
crash on a gherkin child kind newer than this version) with a catch-all
case that emits a UserWarning and skips the child. pytest-bdd keeps
working against future gherkin releases; the warning surfaces the gap
without breaking CI.

The arm is now reachable and exercised by a test, so the pragma: no cover
markers are dropped.
@Pierre-Sassoulas Pierre-Sassoulas force-pushed the refactor/match-gherkin-child branch from 91f5eb9 to b837fc9 Compare July 6, 2026 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants