From fe22ebdb98d125db840c980cbd953a8fbc2e5cdf Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 29 Nov 2016 18:51:05 -0500 Subject: Use `atpublic` for @public instead of internal copy. --- setup.py | 1 + 1 file changed, 1 insertion(+) (limited to 'setup.py') diff --git a/setup.py b/setup.py index 2d182cdc7..080c83925 100644 --- a/setup.py +++ b/setup.py @@ -106,6 +106,7 @@ case second `m'. Any other spelling is incorrect.""", install_requires = [ 'aiosmtpd', 'alembic', + 'atpublic', 'falcon>=1.0.0rc1', 'flufl.bounce', 'flufl.i18n', -- cgit v1.2.3-70-g09d2 From c44c48e1447b54f2f8673a930b343767f0b917fb Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 29 Nov 2016 19:19:13 -0500 Subject: Use flufl.testing's flake8 plugin. --- setup.py | 1 - src/mailman/testing/flake8.py | 141 ------------------------------------------ tox.ini | 2 + 3 files changed, 2 insertions(+), 142 deletions(-) delete mode 100644 src/mailman/testing/flake8.py (limited to 'setup.py') diff --git a/setup.py b/setup.py index 080c83925..378bc989a 100644 --- a/setup.py +++ b/setup.py @@ -101,7 +101,6 @@ case second `m'. Any other spelling is incorrect.""", include_package_data = True, entry_points = { 'console_scripts' : list(scripts), - 'flake8.extension': ['B4 = mailman.testing.flake8:ImportOrder'], }, install_requires = [ 'aiosmtpd', diff --git a/src/mailman/testing/flake8.py b/src/mailman/testing/flake8.py deleted file mode 100644 index 68bd37761..000000000 --- a/src/mailman/testing/flake8.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright (C) 2016 by the Free Software Foundation, Inc. -# -# This file is part of GNU Mailman. -# -# GNU Mailman is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) -# any later version. -# -# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. -# -# You should have received a copy of the GNU General Public License along with -# GNU Mailman. If not, see . - -"""Flake8 extensions for Mailman coding style.""" - - -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') - - -NONFROM_FOLLOWS_FROM = 'B401 Non-from import follows from-import' -NONFROM_MULTIPLE_NAMES = 'B402 Multiple names on non-from import' -NONFROM_SHORTER_FOLLOWS = 'B403 Shorter non-from import follows longer' -NONFROM_ALPHA_UNSORTED = ( - 'B404 Same-length non-from imports not sorted alphabetically') -NONFROM_EXTRA_BLANK_LINE = ( - 'B405 Unexpected blank line since last non-from import') -NONFROM_DOTTED_UNSORTED = ( - 'B406 Dotted non-from import not sorted alphabetically') - -FROMIMPORT_MISSING_BLANK_LINE = ( - 'B411 Expected one blank line since last non-from import') -FROMIMPORT_ALPHA_UNSORTED = 'B412 from-import not sorted alphabetically' -FROMIMPORT_MULTIPLE = 'B413 Multiple from-imports of same module' -FROMIMPORT_NAMES_UNSORTED = ( - 'B414 from-imported names are not sorted alphabetically') - - -class ImportVisitor(NodeVisitor): - def __init__(self): - 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: - name = 'flufl-import-order' - version = '0.1' - - def __init__(self, tree, filename): - self.tree = tree - self.filename = filename - - def _error(self, record, error): - code, space, text = error.partition(' ') - return (record.lineno, record.colno, - '{} {}'.format(code, text), ImportOrder) - - def run(self): - 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, NONFROM_MULTIPLE_NAMES) - if last_import.itype is ImportType.from_import: - yield self._error(record, NONFROM_FOLLOWS_FROM) - # Shorter imports should always precede longer import *except* - # when they are dotted imports and everything but the last - # path component are the same. In that case, they should be - # sorted alphabetically. - last_name = last_import.names[0] - this_name = record.names[0] - if '.' in last_name and '.' in this_name: - last_parts = last_name.split('.') - this_parts = this_name.split('.') - if (last_parts[:-1] == this_parts[:-1] and - last_parts[-1] > this_parts[-1]): - yield self._error(record, NONFROM_DOTTED_UNSORTED) - elif len(last_name) > len(this_name): - yield self._error(record, NONFROM_SHORTER_FOLLOWS) - # 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, NONFROM_ALPHA_UNSORTED) - if last_import.lineno + 1 != record.lineno: - yield self._error(record, NONFROM_DOTTED_UNSORTED) - 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, FROMIMPORT_MISSING_BLANK_LINE) - if last_import.itype is ImportType.non_from: - last_import = record - continue - if last_import.module > record.module: - yield self._error(record, FROMIMPORT_ALPHA_UNSORTED) - # All imports from the same module should show up in the same - # multiline import. - if last_import.module == record.module: - yield self._error(record, FROMIMPORT_MULTIPLE) - # Check the sort order of the imported names. - if sorted(record.names) != record.names: - yield self._error(record, FROMIMPORT_NAMES_UNSORTED) - # How to check for no blank lines between from imports? - # Update the last import. - last_import = record diff --git a/tox.ini b/tox.ini index 7892b557a..34bd367d1 100644 --- a/tox.ini +++ b/tox.ini @@ -35,6 +35,7 @@ commands = python -m flake8 src deps = flake8>3.0 + flufl.testing [testenv:docs] basepython = python3 @@ -51,3 +52,4 @@ rc = --rcfile={[coverage]rcfile} max-line-length = 79 exclude = src/mailman/compat/*.py jobs = 1 +enable-extensions = U4 -- cgit v1.2.3-70-g09d2 From cdd24f88bd36aa2bd87252618e98b80aa7cc1bf1 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 29 Nov 2016 19:58:16 -0500 Subject: More fixes & flufl.testing. --- setup.py | 1 + src/mailman/core/system.py | 3 +- src/mailman/testing/nose.py | 118 -------------------------------------------- unittest.cfg | 5 +- 4 files changed, 7 insertions(+), 120 deletions(-) delete mode 100644 src/mailman/testing/nose.py (limited to 'setup.py') diff --git a/setup.py b/setup.py index 378bc989a..c31817b9a 100644 --- a/setup.py +++ b/setup.py @@ -110,6 +110,7 @@ case second `m'. Any other spelling is incorrect.""", 'flufl.bounce', 'flufl.i18n', 'flufl.lock', + 'flufl.testing', 'httplib2', 'lazr.config', 'nose2', diff --git a/src/mailman/core/system.py b/src/mailman/core/system.py index 2d76f0c97..41ddced95 100644 --- a/src/mailman/core/system.py +++ b/src/mailman/core/system.py @@ -19,8 +19,9 @@ import sys +from mailman import version from mailman.interfaces.system import ISystem -from public import public, version +from public import public from zope.interface import implementer diff --git a/src/mailman/testing/nose.py b/src/mailman/testing/nose.py deleted file mode 100644 index 1a9830da5..000000000 --- a/src/mailman/testing/nose.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (C) 2013-2016 by the Free Software Foundation, Inc. -# -# This file is part of GNU Mailman. -# -# GNU Mailman is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) -# any later version. -# -# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. -# -# You should have received a copy of the GNU General Public License along with -# GNU Mailman. If not, see . - -"""nose2 test infrastructure.""" - -import os -import re -import doctest -import importlib - -from mailman.testing.documentation import setup, teardown -from mailman.testing.layers import ConfigLayer, MockAndMonkeyLayer, SMTPLayer -from nose2.events import Plugin -from pkg_resources import resource_filename -from public import public - - -DOT = '.' -FLAGS = doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.REPORT_NDIFF -TOPDIR = os.path.dirname(resource_filename('mailman', '__init__.py')) - - -@public -class NosePlugin(Plugin): - configSection = 'mailman' - - def __init__(self): - super().__init__() - self.patterns = [] - self.stderr = False - def set_stderr(ignore): # noqa: E306 - self.stderr = True - self.addArgument(self.patterns, 'P', 'pattern', - 'Add a test matching pattern') - self.addFlag(set_stderr, 'E', 'stderr', - 'Enable stderr logging to sub-runners') - - def startTestRun(self, event): - MockAndMonkeyLayer.testing_mode = True - if (self.stderr or - len(os.environ.get('MM_VERBOSE_TESTLOG', '').strip()) > 0): - ConfigLayer.stderr = True - - def getTestCaseNames(self, event): - if len(self.patterns) == 0: - # No filter patterns, so everything should be tested. - return - # Does the pattern match the fully qualified class name? - for pattern in self.patterns: - full_class_name = '{}.{}'.format( - event.testCase.__module__, event.testCase.__name__) - if re.search(pattern, full_class_name): - # Don't suppress this test class. - return - names = filter(event.isTestMethod, dir(event.testCase)) - for name in names: - full_test_name = '{}.{}.{}'.format( - event.testCase.__module__, - event.testCase.__name__, - name) - for pattern in self.patterns: - if re.search(pattern, full_test_name): - break - else: - event.excludedNames.append(name) - - def handleFile(self, event): - path = event.path[len(TOPDIR)+1:] - if len(self.patterns) > 0: - for pattern in self.patterns: - if re.search(pattern, path): - break - else: - # Skip this doctest. - return - base, ext = os.path.splitext(path) - if ext != '.rst': - return - # Look to see if the package defines a test layer, otherwise use the - # default layer. First turn the file system path into a dotted Python - # module path. - parent = os.path.dirname(path) - dotted = 'mailman.' + DOT.join(parent.split(os.path.sep)) - try: - module = importlib.import_module(dotted) - except ImportError: - layer = SMTPLayer - else: - layer = getattr(module, 'layer', SMTPLayer) - test = doctest.DocFileTest( - path, package='mailman', - optionflags=FLAGS, - setUp=setup, - tearDown=teardown) - test.layer = layer - # Suppress the extra "Doctest: ..." line. - test.shortDescription = lambda: None - event.extraTests.append(test) - - # def startTest(self, event): - # import sys; print('vvvvv', event.test, file=sys.stderr) - - # def stopTest(self, event): - # import sys; print('^^^^^', event.test, file=sys.stderr) diff --git a/unittest.cfg b/unittest.cfg index d639a8ae3..4b080f22e 100644 --- a/unittest.cfg +++ b/unittest.cfg @@ -1,6 +1,6 @@ [unittest] verbose = 2 -plugins = mailman.testing.nose +plugins = flufl.testing.nose nose2.plugins.layers [mailman] @@ -8,3 +8,6 @@ always-on = True [log-capture] always-on = False + +[flufl.testing] +package = mailman -- cgit v1.2.3-70-g09d2