diff options
| author | Barry Warsaw | 2016-03-27 22:14:47 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2016-03-27 22:14:47 -0400 |
| commit | 532045c68c6325fa90d74572b6c035c666470849 (patch) | |
| tree | e5acc8183a22cb03ac30d155675904fb23ca05fb /src/mailman/testing/flake8.py | |
| parent | d13e7ece87ceefadcb5273e93e5b0caeade0e600 (diff) | |
| download | mailman-532045c68c6325fa90d74572b6c035c666470849.tar.gz mailman-532045c68c6325fa90d74572b6c035c666470849.tar.zst mailman-532045c68c6325fa90d74572b6c035c666470849.zip | |
Diffstat (limited to 'src/mailman/testing/flake8.py')
| -rw-r--r-- | src/mailman/testing/flake8.py | 88 |
1 files changed, 85 insertions, 3 deletions
diff --git a/src/mailman/testing/flake8.py b/src/mailman/testing/flake8.py index d002e8b6c..df4e39c90 100644 --- a/src/mailman/testing/flake8.py +++ b/src/mailman/testing/flake8.py @@ -19,17 +19,39 @@ from ast import NodeVisitor +from collections import namedtuple +from enum import Enum + + +class ImportType(Enum): + non_from = 0 + from_import = 1 + + +ImportRecord = namedtuple('ImportRecord', 'itype lineno colno, module, names') class ImportVisitor(NodeVisitor): def __init__(self): - self.last_import = None + self.imports = [] def visit_Import(self, node): if node.col_offset != 0: # Ignore nested imports. return - + names = [alias.name for alias in node.names] + self.imports.append( + ImportRecord(ImportType.non_from, node.lineno, node.col_offset, + None, names)) + + def visit_ImportFrom(self, node): + if node.col_offset != 0: + # Ignore nested imports. + return + names = [alias.name for alias in node.names] + self.imports.append( + ImportRecord(ImportType.from_import, node.lineno, node.col_offset, + node.module, names)) class ImportOrder: @@ -40,5 +62,65 @@ class ImportOrder: self.tree = tree self.filename = filename + def _error(self, record, code, text): + return (record.lineno, record.colno, + '{} {}'.format(code, text), ImportOrder) + def run(self): - print('Running', self.name, self.version) + visitor = ImportVisitor() + visitor.visit(self.tree) + last_import = None + for record in visitor.imports: + if last_import is None: + last_import = record + continue + if record.itype is ImportType.non_from: + if len(record.names) != 1: + yield self._error(record, 'B402', + 'Multiple names on non-from import') + if last_import.itype is ImportType.from_import: + yield self._error(record, 'B401', + 'Non-from import follows from-import') + if len(last_import.names[0]) > len(record.names[0]): + yield self._error( + record, 'B403', + 'Shorter non-from import follows longer') + # It's also possible that the imports are the same length, in + # which case they must be sorted alphabetically. + if (len(last_import.names[0]) == len(record.names[0]) and + last_import.names[0] > record.names[0]): + yield self._error( + record, 'B404', + 'Non-from imports not alphabetically sorted') + if last_import.lineno + 1 != record.lineno: + yield self._error( + record, 'B405', + 'Unexpected blank line since last non-from import') + else: + assert record.itype is ImportType.from_import + if (last_import.itype is ImportType.non_from and + record.lineno != last_import.lineno + 2): + yield self._error( + record, 'B406', + 'Expected one blank line since last non-from import') + if last_import.itype is ImportType.non_from: + last_import = record + continue + if last_import.module > record.module: + yield self._error( + record, 'B407', + 'From-imports not sorted alphabetically') + # All imports from the same module should show up in the same + # multiline import. + if last_import.module == record.module: + yield self._error( + record, 'B408', + 'Importing from same module on different lines') + # Check the sort order of the imported names. + if sorted(record.names) != record.names: + yield self._error( + record, 'B409', + 'Imported names are not sorted alphabetically') + # How to check for no blank lines between from imports? + # Update the last import. + last_import = record |
