Provide a tokeneater() method to detect the end of a code block.
| 1164 | class EndOfBlock(Exception): pass |
| 1165 | |
| 1166 | class BlockFinder: |
| 1167 | """Provide a tokeneater() method to detect the end of a code block.""" |
| 1168 | def __init__(self): |
| 1169 | self.indent = 0 |
| 1170 | self.islambda = False |
| 1171 | self.started = False |
| 1172 | self.passline = False |
| 1173 | self.indecorator = False |
| 1174 | self.last = 1 |
| 1175 | self.body_col0 = None |
| 1176 | |
| 1177 | def tokeneater(self, type, token, srowcol, erowcol, line): |
| 1178 | if not self.started and not self.indecorator: |
| 1179 | # skip any decorators |
| 1180 | if token == "@": |
| 1181 | self.indecorator = True |
| 1182 | # look for the first "def", "class" or "lambda" |
| 1183 | elif token in ("def", "class", "lambda"): |
| 1184 | if token == "lambda": |
| 1185 | self.islambda = True |
| 1186 | self.started = True |
| 1187 | self.passline = True # skip to the end of the line |
| 1188 | elif type == tokenize.NEWLINE: |
| 1189 | self.passline = False # stop skipping when a NEWLINE is seen |
| 1190 | self.last = srowcol[0] |
| 1191 | if self.islambda: # lambdas always end at the first NEWLINE |
| 1192 | raise EndOfBlock |
| 1193 | # hitting a NEWLINE when in a decorator without args |
| 1194 | # ends the decorator |
| 1195 | if self.indecorator: |
| 1196 | self.indecorator = False |
| 1197 | elif self.passline: |
| 1198 | pass |
| 1199 | elif type == tokenize.INDENT: |
| 1200 | if self.body_col0 is None and self.started: |
| 1201 | self.body_col0 = erowcol[1] |
| 1202 | self.indent = self.indent + 1 |
| 1203 | self.passline = True |
| 1204 | elif type == tokenize.DEDENT: |
| 1205 | self.indent = self.indent - 1 |
| 1206 | # the end of matching indent/dedent pairs end a block |
| 1207 | # (note that this only works for "def"/"class" blocks, |
| 1208 | # not e.g. for "if: else:" or "try: finally:" blocks) |
| 1209 | if self.indent <= 0: |
| 1210 | raise EndOfBlock |
| 1211 | elif type == tokenize.COMMENT: |
| 1212 | if self.body_col0 is not None and srowcol[1] >= self.body_col0: |
| 1213 | # Include comments if indented at least as much as the block |
| 1214 | self.last = srowcol[0] |
| 1215 | elif self.indent == 0 and type not in (tokenize.COMMENT, tokenize.NL): |
| 1216 | # any other token on the same indentation level end the previous |
| 1217 | # block as well, except the pseudo-tokens COMMENT and NL. |
| 1218 | raise EndOfBlock |
| 1219 | |
| 1220 | def getblock(lines): |
| 1221 | """Extract the block of code at the top of the given list of lines.""" |