55import textwrap
66import typing
77from dataclasses import dataclass , field
8- from typing import Any , Dict , List , Optional
8+ from typing import Any
99
10- from gherkin .errors import CompositeParserException
11- from gherkin .parser import Parser
12- from gherkin .token_scanner import TokenScanner
10+ from gherkin .errors import CompositeParserException # type: ignore
11+ from gherkin .parser import Parser # type: ignore
1312
1413from . import exceptions
1514
1615if typing .TYPE_CHECKING :
17- from typing import Self
16+ from typing_extensions import Self
1817
1918
2019ERROR_PATTERNS = [
3332 exceptions .BackgroundError ,
3433 "Multiple 'Background' sections detected. Only one 'Background' is allowed per feature." ,
3534 ),
36- (
37- re .compile (r"expected:.*got 'Scenario Outline.*'" ),
38- exceptions .ScenarioOutlineError ,
39- "'Scenario Outline' requires steps before 'Examples'." ,
40- ),
4135 (
4236 re .compile (r"expected:.*got 'Scenario.*'" ),
4337 exceptions .ScenarioError ,
44- "Misplaced or incorrect 'Scenario' keyword. Ensure it's correctly placed." ,
45- ),
46- (
47- re .compile (r"expected:.*got 'Examples.*'" ),
48- exceptions .ExamplesError ,
49- "'Examples' must follow a valid 'Scenario Outline' and contain table rows." ,
38+ "Misplaced or incorrect 'Scenario' keyword. Ensure it's correctly placed. There might be a missing Feature section." ,
5039 ),
5140 (
5241 re .compile (r"expected:.*got 'Given.*'" ),
5342 exceptions .StepError ,
5443 "Improper step keyword detected. Ensure correct order and indentation for steps (Given, When, Then, etc.)." ,
5544 ),
56- (
57- re .compile (r"expected:.*got 'TagLine.*'" ),
58- exceptions .TagError ,
59- "Tags are misplaced. They should be directly above features, scenarios, or outlines." ,
60- ),
6145 (
6246 re .compile (r"expected:.*got 'Rule.*'" ),
6347 exceptions .RuleError ,
6448 "Misplaced or incorrectly formatted 'Rule'. Ensure it follows the feature structure." ,
6549 ),
66- (
67- re .compile (r"expected:.*got 'DocString.*'" ),
68- exceptions .DocStringError ,
69- 'DocString must be enclosed in triple quotes ("""). Ensure proper formatting.' ,
70- ),
7150 (
7251 re .compile (r"expected:.*got '.*'" ),
7352 exceptions .TokenError ,
@@ -196,7 +175,7 @@ class Scenario:
196175 description : str
197176 steps : list [Step ]
198177 tags : list [Tag ]
199- examples : list [DataTable ] | None = field (default_factory = list )
178+ examples : list [DataTable ] = field (default_factory = list )
200179
201180 @classmethod
202181 def from_dict (cls , data : dict [str , Any ]) -> Self :
@@ -208,7 +187,7 @@ def from_dict(cls, data: dict[str, Any]) -> Self:
208187 description = data ["description" ],
209188 steps = [Step .from_dict (step ) for step in data ["steps" ]],
210189 tags = [Tag .from_dict (tag ) for tag in data ["tags" ]],
211- examples = [DataTable .from_dict (example ) for example in data . get ( "examples" , []) ],
190+ examples = [DataTable .from_dict (example ) for example in data [ "examples" ] ],
212191 )
213192
214193
@@ -309,37 +288,38 @@ def _to_raw_string(normal_string: str) -> str:
309288 return normal_string .replace ("\\ " , "\\ \\ " )
310289
311290
312- def get_gherkin_document (abs_filename : str = None , encoding : str = "utf-8" ) -> GherkinDocument :
291+ def get_gherkin_document (abs_filename : str , encoding : str = "utf-8" ) -> GherkinDocument :
313292 with open (abs_filename , encoding = encoding ) as f :
314293 feature_file_text = f .read ()
315294
316295 try :
317- gherkin_data = Parser ().parse (TokenScanner ( feature_file_text ) )
296+ gherkin_data = Parser ().parse (feature_file_text )
318297 except CompositeParserException as e :
319298 message = e .args [0 ]
320299 line = e .errors [0 ].location ["line" ]
321300 line_content = linecache .getline (abs_filename , e .errors [0 ].location ["line" ]).rstrip ("\n " )
322301 filename = abs_filename
323- gherkin_error_handler = GherkinParserErrorHandler ()
324- gherkin_error_handler (message , line , line_content , filename )
302+ handle_gherkin_parser_error (message , line , line_content , filename , e )
325303 # If no patterns matched, raise a generic GherkinParserError
326- raise exceptions .GherkinParseError (f"Unknown parsing error: { message } " , line , line_content , filename )
304+ raise exceptions .GherkinParseError (f"Unknown parsing error: { message } " , line , line_content , filename ) from e
327305
328306 # At this point, the `gherkin_data` should be valid if no exception was raised
329307 return GherkinDocument .from_dict (gherkin_data )
330308
331309
332- class GherkinParserErrorHandler :
333- """Parses raw Gherkin parser errors and converts them to human-readable exceptions."""
334-
335- def __call__ (self , raw_error : str , line : int , line_content : str , filename : str ):
336- """Map the error message to a specific exception type and raise it."""
337- # Split the raw_error into individual lines
338- error_lines = raw_error .splitlines ()
339-
340- # Check each line against all error patterns
341- for error_line in error_lines :
342- for pattern , exception_class , message in ERROR_PATTERNS :
343- if pattern .search (error_line ):
344- # If a match is found, raise the corresponding exception with the formatted message
310+ def handle_gherkin_parser_error (
311+ raw_error : str , line : int , line_content : str , filename : str , original_exception : Exception | None = None
312+ ):
313+ """Map the error message to a specific exception type and raise it."""
314+ # Split the raw_error into individual lines
315+ error_lines = raw_error .splitlines ()
316+
317+ # Check each line against all error patterns
318+ for error_line in error_lines :
319+ for pattern , exception_class , message in ERROR_PATTERNS :
320+ if pattern .search (error_line ):
321+ # If a match is found, raise the corresponding exception with the formatted message
322+ if original_exception :
323+ raise exception_class (message , line , line_content , filename ) from original_exception
324+ else :
345325 raise exception_class (message , line , line_content , filename )
0 commit comments