1# pylint: disable=protected-access
2
3def indent(func):
4 """
5 Decorator for allowing to use method as normal method or with
6 context manager for auto-indenting code blocks.
7 """
8 def wrapper(self, line, *args, optimize=True, **kwds):
9 last_line = self._indent_last_line
10 line = func(self, line, *args, **kwds)
11 # When two blocks have the same condition (such as value has to be dict),
12 # do the check only once and keep it under one block.
13 merged = optimize and last_line == line
14 if merged:
15 self._code.pop()
16 self._indent_last_line = line
17 return Indent(self, line, merged=merged)
18 return wrapper
19
20
21class Indent:
22 def __init__(self, instance, line, merged=False):
23 self.instance = instance
24 self.line = line
25 self.merged = merged
26
27 def __enter__(self):
28 self.instance._indent += 1
29 # A merged block is a continuation of the block just closed, so it keeps
30 # its scope; otherwise this is a new scope and variables defined in it
31 # are not visible to sibling blocks.
32 if self.merged and self.instance._last_closed_scope is not None:
33 scope = self.instance._last_closed_scope
34 else:
35 self.instance._scope_counter += 1
36 scope = self.instance._scope_counter
37 self.instance._scope_stack.append(scope)
38
39 def __exit__(self, type_, value, traceback):
40 self.instance._indent -= 1
41 self.instance._last_closed_scope = self.instance._scope_stack.pop()
42 self.instance._indent_last_line = self.line