import tokenize
from io import BytesIO
from collections import deque
code_string = r"""
class Bar(object):
def __init__(self):
a = \
1
b = 2
""".strip()
file = BytesIO(code_string.encode())
tokens = deque(tokenize.tokenize(file.readline))
lines = []
while tokens:
token = tokens.popleft()
if token.type == tokenize.NAME and token.string == 'def':
start_line, start_column = token.start
end_line, _ = token.end
enclosures = 0
while tokens:
token = tokens.popleft()
if token.type == tokenize.NL: # ignore empty lines
continue
if token.type == tokenize.OP and token.string in '([{':
enclosures += 1
_, column = token.start
if column <= start_column and token.type != tokenize.INDENT and not enclosures:
tokens.appendleft(token)
break
if token.type == tokenize.OP and token.string in ')]}':
enclosures -= 1
end_line, _ = token.end
lines.append((start_line, end_line))
print(lines)
aW1wb3J0IHRva2VuaXplCmZyb20gaW8gaW1wb3J0IEJ5dGVzSU8KZnJvbSBjb2xsZWN0aW9ucyBpbXBvcnQgZGVxdWUKY29kZV9zdHJpbmcgPSByIiIiCmNsYXNzIEJhcihvYmplY3QpOgogIGRlZiBfX2luaXRfXyhzZWxmKToKICAgIGEgPSBcCjEKICAgIGIgPSAyCiIiIi5zdHJpcCgpCmZpbGUgPSBCeXRlc0lPKGNvZGVfc3RyaW5nLmVuY29kZSgpKQp0b2tlbnMgPSBkZXF1ZSh0b2tlbml6ZS50b2tlbml6ZShmaWxlLnJlYWRsaW5lKSkKbGluZXMgPSBbXQp3aGlsZSB0b2tlbnM6CiAgICB0b2tlbiA9IHRva2Vucy5wb3BsZWZ0KCkKICAgIGlmIHRva2VuLnR5cGUgPT0gdG9rZW5pemUuTkFNRSBhbmQgdG9rZW4uc3RyaW5nID09ICdkZWYnOgogICAgICAgIHN0YXJ0X2xpbmUsIHN0YXJ0X2NvbHVtbiA9IHRva2VuLnN0YXJ0CiAgICAgICAgZW5kX2xpbmUsIF8gPSB0b2tlbi5lbmQKICAgICAgICBlbmNsb3N1cmVzID0gMAogICAgICAgIHdoaWxlIHRva2VuczoKICAgICAgICAgICAgdG9rZW4gPSB0b2tlbnMucG9wbGVmdCgpCiAgICAgICAgICAgIGlmIHRva2VuLnR5cGUgPT0gdG9rZW5pemUuTkw6ICMgaWdub3JlIGVtcHR5IGxpbmVzCiAgICAgICAgICAgICAgICBjb250aW51ZQogICAgICAgICAgICBpZiB0b2tlbi50eXBlID09IHRva2VuaXplLk9QIGFuZCB0b2tlbi5zdHJpbmcgaW4gJyhbeyc6CiAgICAgICAgICAgICAgICBlbmNsb3N1cmVzICs9IDEKICAgICAgICAgICAgXywgY29sdW1uID0gdG9rZW4uc3RhcnQKICAgICAgICAgICAgaWYgY29sdW1uIDw9IHN0YXJ0X2NvbHVtbiBhbmQgdG9rZW4udHlwZSAhPSB0b2tlbml6ZS5JTkRFTlQgYW5kIG5vdCBlbmNsb3N1cmVzOgogICAgICAgICAgICAgICAgdG9rZW5zLmFwcGVuZGxlZnQodG9rZW4pCiAgICAgICAgICAgICAgICBicmVhawogICAgICAgICAgICBpZiB0b2tlbi50eXBlID09IHRva2VuaXplLk9QIGFuZCB0b2tlbi5zdHJpbmcgaW4gJyldfSc6CiAgICAgICAgICAgICAgICBlbmNsb3N1cmVzIC09IDEKICAgICAgICAgICAgZW5kX2xpbmUsIF8gPSB0b2tlbi5lbmQKICAgICAgICBsaW5lcy5hcHBlbmQoKHN0YXJ0X2xpbmUsIGVuZF9saW5lKSkKcHJpbnQobGluZXMp