sourcery
sourcery copied to clipboard
Merge repeated with statements into single with
Issue description or question
In unit tests of a legacy system I often come across code like:
with patch('...', return_value='whatever'):
with patch.object(LocalObj, 'get_...', return_value=expected_data) as mocked_x:
with patch('..', return_value=gen_link) as mocked_y:
which imho could be refactored to:
with patch('...', return_value='whatever'), \
patch.object(LocalObj, 'get_...', return_value=expected_data) as mocked_x, \
patch('..', return_value=gen_link) as mocked_y:
This does nicely reduce nesting, and doing a little digging does look like they're exactly equivalent. Just a little concerned about nice formatting of the conjoined with statements.
Formatting context managers statements is a bit of a bother with python < 3.10 where you can at last use parenthesis for multiple with statements https://docs.python.org/3.10/whatsnew/3.10.html#parenthesized-context-managers
until 3.10 is mainstream, the , \ continuation is probably the best solution
Note that we can now handle this internally so have added it to our roadmap. In the meantime this custom rule will handle the case of merging two withs, though not multiple ones:
- id: merge-withs
description: Merge nested with statements
pattern: |
with ${thing} as ${a?}:
with ${next_thing} as ${b?}:
${body}
replacement: |
with (${thing} as ${a}, ${next_thing} as ${b}):
${body}