Rewrites the block_stack management in fix_python_indentation to correctly
handle try...except...finally blocks in all configurations:
- Single except clause
- Multiple except clauses for the same try
- try...except...finally
- Nested try blocks with outer except
- Nested try with inner finally followed by outer except (critical regression)
Root cause: The original implementation stored only keyword strings in
block_stack (e.g. 'try', 'class', 'other'). Two bugs existed:
1. When 'finally:' was encountered, the code popped the 'try' from the
stack. This meant subsequent 'except:' clauses for an outer try could
not find their matching try, producing syntactically invalid Python.
2. The dedent calculation for 'except'/'finally' counted all stack entries
up to the first 'try', but did not account for nested try blocks where
the inner try's except/finally had already been processed.
Fix: Replace the flat string stack with a tuple stack of
(keyword, base_indent, has_seen_except_finally). The new logic:
- 'finally:' always belongs to the innermost try. Pop non-try entries,
set indent to that try's base_indent, then pop the try itself (finally
closes the block). The outer try remains on the stack.
- 'except:' belongs to the innermost try. If the innermost try has already
seen an except/finally AND there is an outer try, the inner try is done:
pop it and use the outer try instead. Otherwise use the innermost try
and mark it as having seen an except/finally clause.
Adds a Behave BDD feature (tdd_indentation_library_try_except.feature)
with 6 scenarios covering all acceptance criteria from issue #2845.
ISSUES CLOSED: #2845