Provide a tokeneater() method to detect the end of a code block.
| 1078 | class EndOfBlock(Exception): pass |
| 1079 | |
| 1080 | class BlockFinder: |
| 1081 | """Provide a tokeneater() method to detect the end of a code block.""" |
| 1082 | def __init__(self): |
| 1083 | self.indent = 0 |
| 1084 | self.singleline = False |
| 1085 | self.started = False |
| 1086 | self.passline = False |
| 1087 | self.indecorator = False |
| 1088 | self.last = 1 |
| 1089 | self.body_col0 = None |
| 1090 | |
| 1091 | def tokeneater(self, type, token, srowcol, erowcol, line): |
| 1092 | if not self.started and not self.indecorator: |
| 1093 | if type in (tokenize.INDENT, tokenize.COMMENT, tokenize.NL): |
| 1094 | pass |
| 1095 | elif token == "async": |
| 1096 | pass |
| 1097 | # skip any decorators |
| 1098 | elif token == "@": |
| 1099 | self.indecorator = True |
| 1100 | else: |
| 1101 | # For "def" and "class" scan to the end of the block. |
| 1102 | # For "lambda" and generator expression scan to |
| 1103 | # the end of the logical line. |
| 1104 | self.singleline = token not in ("def", "class") |
| 1105 | self.started = True |
| 1106 | self.passline = True # skip to the end of the line |
| 1107 | elif type == tokenize.NEWLINE: |
| 1108 | self.passline = False # stop skipping when a NEWLINE is seen |
| 1109 | self.last = srowcol[0] |
| 1110 | if self.singleline: |
| 1111 | raise EndOfBlock |
| 1112 | # hitting a NEWLINE when in a decorator without args |
| 1113 | # ends the decorator |
| 1114 | if self.indecorator: |
| 1115 | self.indecorator = False |
| 1116 | elif self.passline: |
| 1117 | pass |
| 1118 | elif type == tokenize.INDENT: |
| 1119 | if self.body_col0 is None and self.started: |
| 1120 | self.body_col0 = erowcol[1] |
| 1121 | self.indent = self.indent + 1 |
| 1122 | self.passline = True |
| 1123 | elif type == tokenize.DEDENT: |
| 1124 | self.indent = self.indent - 1 |
| 1125 | # the end of matching indent/dedent pairs end a block |
| 1126 | # (note that this only works for "def"/"class" blocks, |
| 1127 | # not e.g. for "if: else:" or "try: finally:" blocks) |
| 1128 | if self.indent <= 0: |
| 1129 | raise EndOfBlock |
| 1130 | elif type == tokenize.COMMENT: |
| 1131 | if self.body_col0 is not None and srowcol[1] >= self.body_col0: |
| 1132 | # Include comments if indented at least as much as the block |
| 1133 | self.last = srowcol[0] |
| 1134 | elif self.indent == 0 and type not in (tokenize.COMMENT, tokenize.NL): |
| 1135 | # any other token on the same indentation level end the previous |
| 1136 | # block as well, except the pseudo-tokens COMMENT and NL. |
| 1137 | raise EndOfBlock |
no outgoing calls
no test coverage detected
searching dependent graphs…