← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/lp-codeimport:pre-commit into lp-codeimport:master

 

Colin Watson has proposed merging ~cjwatson/lp-codeimport:pre-commit into lp-codeimport:master.

Commit message:
Run flake8 and isort from pre-commit

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/lp-codeimport/+git/lp-codeimport/+merge/420015
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/lp-codeimport:pre-commit into lp-codeimport:master.
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..24566fe
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,24 @@
+# See https://pre-commit.com for more information
+# See https://pre-commit.com/hooks.html for more hooks
+repos:
+-   repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v4.2.0
+    hooks:
+    -   id: check-added-large-files
+    -   id: check-ast
+    -   id: check-merge-conflict
+    -   id: check-json
+    -   id: check-xml
+    -   id: check-yaml
+    -   id: debug-statements
+        exclude: systemdocs\.py
+    -   id: no-commit-to-branch
+-   repo: https://github.com/PyCQA/flake8
+    rev: 4.0.1
+    hooks:
+    -   id: flake8
+        exclude: ^lib/contrib/
+-   repo: https://github.com/PyCQA/isort
+    rev: 5.10.1
+    hooks:
+    -   id: isort
diff --git a/_pythonpath.py b/_pythonpath.py
index eab0d63..616dcda 100644
--- a/_pythonpath.py
+++ b/_pythonpath.py
@@ -8,6 +8,7 @@ import imp
 import os.path
 import sys
 
+
 # Get path to this file.
 if __name__ == '__main__':
     filename = __file__
diff --git a/charm/lp-codeimport/reactive/lp-codeimport.py b/charm/lp-codeimport/reactive/lp-codeimport.py
index 60981ff..acfb991 100644
--- a/charm/lp-codeimport/reactive/lp-codeimport.py
+++ b/charm/lp-codeimport/reactive/lp-codeimport.py
@@ -1,7 +1,11 @@
 # Copyright 2018-2021 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
 
 import base64
 import os.path
diff --git a/lib/contrib/glock.py b/lib/contrib/glock.py
index fb835e2..50ae8d7 100644
--- a/lib/contrib/glock.py
+++ b/lib/contrib/glock.py
@@ -21,7 +21,11 @@ Unix.
 @see: class L{GlobalLock} for more details.
 '''
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __version__ = '0.2.' + '$Revision: #5 $'[12:-2]
 __author__ = 'Richard Gruet', 'rjgruet@xxxxxxxxx'
@@ -31,15 +35,21 @@ __doc__ += '\n@author: %s (U{%s})\n@version: %s' % (__author__[0],
                                             __author__[1], __version__)
 __all__ = ['GlobalLock', 'GlobalLockError', 'LockAlreadyAcquired', 'NotOwner']
 
-# Imports:
-import sys, string, os, errno, re
+import errno
+import os
+import re
+import string
+import sys
+
 
 # System-dependent imports for locking implementation:
 _windows = (sys.platform == 'win32')
 
 if _windows:
     try:
-        import win32event, win32api, pywintypes
+        import pywintypes
+        import win32api
+        import win32event
     except ImportError:
         sys.stderr.write('The win32 extensions need to be installed!')
     try:
@@ -279,7 +289,8 @@ def test():
     else: raise Exception('should have raised a NotOwner exception')
 
     # Check that <> threads of same process do block:
-    import threading, time
+    import threading
+    import time
     thread = threading.Thread(target=threadMain, args=(l,))
     print('main: locking...', end='')
     l.acquire()
diff --git a/lib/devscripts/sourcecode.py b/lib/devscripts/sourcecode.py
index 5dc0e6d..0ab7650 100644
--- a/lib/devscripts/sourcecode.py
+++ b/lib/devscripts/sourcecode.py
@@ -3,7 +3,11 @@
 
 """Tools for maintaining the Launchpad source code."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 __all__ = [
@@ -19,6 +23,7 @@ import os
 import shutil
 import sys
 
+
 try:
     from breezy import ui
     from breezy.branch import Branch
diff --git a/lib/devscripts/tests/test_sourcecode.py b/lib/devscripts/tests/test_sourcecode.py
index ef03676..368ea0d 100644
--- a/lib/devscripts/tests/test_sourcecode.py
+++ b/lib/devscripts/tests/test_sourcecode.py
@@ -3,7 +3,11 @@
 
 """Module docstring goes here."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 
@@ -12,6 +16,7 @@ import shutil
 import tempfile
 import unittest
 
+
 try:
     from breezy.bzr.bzrdir import BzrDir
     from breezy.tests import TestCase
@@ -20,6 +25,7 @@ except ImportError:
     from bzrlib.bzrdir import BzrDir
     from bzrlib.tests import TestCase
     from bzrlib.transport import get_transport
+
 import six
 
 from devscripts import get_launchpad_root
diff --git a/lib/lp/codehosting/__init__.py b/lib/lp/codehosting/__init__.py
index 2a20dfa..ad12914 100644
--- a/lib/lp/codehosting/__init__.py
+++ b/lib/lp/codehosting/__init__.py
@@ -7,7 +7,11 @@ NOTE: Importing this package will load any system Bazaar plugins, as well as
 all plugins in the bzrplugins/ directory underneath the rocketfuel checkout.
 """
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 __all__ = [
@@ -21,6 +25,7 @@ import six
 
 from lp.services.config import config
 
+
 if six.PY2:
     from bzrlib.plugin import load_plugins as bzr_load_plugins
     # This import is needed so that bzr's logger gets registered.
diff --git a/lib/lp/codehosting/codeimport/dispatcher.py b/lib/lp/codehosting/codeimport/dispatcher.py
index 69bdbb0..dfc22f4 100644
--- a/lib/lp/codehosting/codeimport/dispatcher.py
+++ b/lib/lp/codehosting/codeimport/dispatcher.py
@@ -8,7 +8,11 @@ imports need to be processed and launching child processes to handle
 them.
 """
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 __all__ = [
diff --git a/lib/lp/codehosting/codeimport/foreigntree.py b/lib/lp/codehosting/codeimport/foreigntree.py
index e0fbe43..2ef672c 100644
--- a/lib/lp/codehosting/codeimport/foreigntree.py
+++ b/lib/lp/codehosting/codeimport/foreigntree.py
@@ -3,7 +3,11 @@
 
 """Support for CVS branches."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 __all__ = ['CVSWorkingTree']
diff --git a/lib/lp/codehosting/codeimport/tarball.py b/lib/lp/codehosting/codeimport/tarball.py
index edabfbb..4ed7188 100644
--- a/lib/lp/codehosting/codeimport/tarball.py
+++ b/lib/lp/codehosting/codeimport/tarball.py
@@ -3,7 +3,11 @@
 
 """Create and extract tarballs."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 __all__ = ['create_tarball', 'extract_tarball', 'TarError']
diff --git a/lib/lp/codehosting/codeimport/tests/helpers.py b/lib/lp/codehosting/codeimport/tests/helpers.py
index 8c07f02..e7c9103 100644
--- a/lib/lp/codehosting/codeimport/tests/helpers.py
+++ b/lib/lp/codehosting/codeimport/tests/helpers.py
@@ -3,7 +3,11 @@
 
 """Helpers for lp.codehosting.codeimport tests."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 
diff --git a/lib/lp/codehosting/codeimport/tests/servers.py b/lib/lp/codehosting/codeimport/tests/servers.py
index 06ad6c1..f5f444b 100644
--- a/lib/lp/codehosting/codeimport/tests/servers.py
+++ b/lib/lp/codehosting/codeimport/tests/servers.py
@@ -3,7 +3,11 @@
 
 """Server classes that know how to create various kinds of foreign archive."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __all__ = [
     'BzrServer',
diff --git a/lib/lp/codehosting/codeimport/tests/test_dispatcher.py b/lib/lp/codehosting/codeimport/tests/test_dispatcher.py
index a6dc82e..7e974f9 100644
--- a/lib/lp/codehosting/codeimport/tests/test_dispatcher.py
+++ b/lib/lp/codehosting/codeimport/tests/test_dispatcher.py
@@ -3,7 +3,11 @@
 
 """Tests for the code import dispatcher."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 
diff --git a/lib/lp/codehosting/codeimport/tests/test_foreigntree.py b/lib/lp/codehosting/codeimport/tests/test_foreigntree.py
index 161f83b..ff378ff 100644
--- a/lib/lp/codehosting/codeimport/tests/test_foreigntree.py
+++ b/lib/lp/codehosting/codeimport/tests/test_foreigntree.py
@@ -3,7 +3,11 @@
 
 """Tests for foreign branch support."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 
diff --git a/lib/lp/codehosting/codeimport/tests/test_uifactory.py b/lib/lp/codehosting/codeimport/tests/test_uifactory.py
index 5c93900..4873bb3 100644
--- a/lib/lp/codehosting/codeimport/tests/test_uifactory.py
+++ b/lib/lp/codehosting/codeimport/tests/test_uifactory.py
@@ -3,7 +3,11 @@
 
 """Tests for `LoggingUIFactory`."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 
diff --git a/lib/lp/codehosting/codeimport/tests/test_worker.py b/lib/lp/codehosting/codeimport/tests/test_worker.py
index a47c864..89a1212 100644
--- a/lib/lp/codehosting/codeimport/tests/test_worker.py
+++ b/lib/lp/codehosting/codeimport/tests/test_worker.py
@@ -3,7 +3,11 @@
 
 """Tests for the code import worker."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 
diff --git a/lib/lp/codehosting/codeimport/tests/test_workermonitor.py b/lib/lp/codehosting/codeimport/tests/test_workermonitor.py
index d9f672f..56b3112 100644
--- a/lib/lp/codehosting/codeimport/tests/test_workermonitor.py
+++ b/lib/lp/codehosting/codeimport/tests/test_workermonitor.py
@@ -3,7 +3,11 @@
 
 """Tests for the CodeImportWorkerMonitor and related classes."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 
diff --git a/lib/lp/codehosting/codeimport/uifactory.py b/lib/lp/codehosting/codeimport/uifactory.py
index 2b8bfd8..1ac52c2 100644
--- a/lib/lp/codehosting/codeimport/uifactory.py
+++ b/lib/lp/codehosting/codeimport/uifactory.py
@@ -3,7 +3,11 @@
 
 """A UIFactory useful for code imports."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 __all__ = ['LoggingUIFactory']
diff --git a/lib/lp/codehosting/codeimport/worker.py b/lib/lp/codehosting/codeimport/worker.py
index 9391324..9bd9444 100644
--- a/lib/lp/codehosting/codeimport/worker.py
+++ b/lib/lp/codehosting/codeimport/worker.py
@@ -3,7 +3,11 @@
 
 """The code import worker. This imports code from foreign repositories."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 __all__ = [
@@ -28,9 +32,10 @@ import shutil
 import signal
 import subprocess
 
+
 # FIRST Ensure correct plugins are loaded. Do not delete this comment or the
 # line below this comment.
-import lp.codehosting  # noqa: F401
+import lp.codehosting  # noqa: F401  # isort: split
 
 from bzrlib.branch import (
     Branch,
@@ -840,7 +845,9 @@ class GitImportWorker(PullingImportWorker):
     def probers(self):
         """See `PullingImportWorker.probers`."""
         from bzrlib.plugins.git import (
-            LocalGitProber, RemoteGitProber)
+            LocalGitProber,
+            RemoteGitProber,
+            )
         return [LocalGitProber, RemoteGitProber]
 
     def getRevisionLimit(self):
@@ -974,7 +981,10 @@ class BzrImportWorker(PullingImportWorker):
     @property
     def probers(self):
         """See `PullingImportWorker.probers`."""
-        from bzrlib.bzrdir import BzrProber, RemoteBzrProber
+        from bzrlib.bzrdir import (
+            BzrProber,
+            RemoteBzrProber,
+            )
         return [BzrProber, RemoteBzrProber]
 
 
diff --git a/lib/lp/codehosting/codeimport/workermonitor.py b/lib/lp/codehosting/codeimport/workermonitor.py
index 6fa304b..d9aa409 100644
--- a/lib/lp/codehosting/codeimport/workermonitor.py
+++ b/lib/lp/codehosting/codeimport/workermonitor.py
@@ -3,7 +3,11 @@
 
 """Code to talk to the database about what the worker script is doing."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 __all__ = []
diff --git a/lib/lp/codehosting/tests/helpers.py b/lib/lp/codehosting/tests/helpers.py
index 0479b1a..7d84e11 100644
--- a/lib/lp/codehosting/tests/helpers.py
+++ b/lib/lp/codehosting/tests/helpers.py
@@ -3,7 +3,11 @@
 
 """Common helpers for codehosting tests."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 __all__ = [
diff --git a/lib/lp/scripts/utilities/importpedant.py b/lib/lp/scripts/utilities/importpedant.py
index eac533f..fa7dee8 100644
--- a/lib/lp/scripts/utilities/importpedant.py
+++ b/lib/lp/scripts/utilities/importpedant.py
@@ -1,7 +1,11 @@
 # Copyright 2009-2020 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
 
 import atexit
 import itertools
diff --git a/lib/lp/scripts/utilities/test.py b/lib/lp/scripts/utilities/test.py
index b4988f5..738b713 100755
--- a/lib/lp/scripts/utilities/test.py
+++ b/lib/lp/scripts/utilities/test.py
@@ -13,7 +13,11 @@
 ##############################################################################
 """Test script."""
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
 
 import argparse
 import doctest
@@ -168,11 +172,11 @@ def main():
     # The imports at the top of this file must avoid anything that reads
     # from Launchpad config. Now that we've set the correct config instance,
     # we can safely import the rest.
+    from lp.services.testing import profiled
     from lp.services.testing.customresult import (
         filter_tests,
         patch_find_tests,
         )
-    from lp.services.testing import profiled
 
     # Extract arguments so we can see them too. We need to strip
     # --resume-layer and --default stuff if found as get_options can't
diff --git a/lib/lp/scripts/utilities/tests/test_shhh.py b/lib/lp/scripts/utilities/tests/test_shhh.py
index c00e2f8..8d95ddb 100644
--- a/lib/lp/scripts/utilities/tests/test_shhh.py
+++ b/lib/lp/scripts/utilities/tests/test_shhh.py
@@ -1,7 +1,12 @@
 # Copyright 2009 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
 
 __metaclass__ = type
 
diff --git a/lib/lp/scripts/utilities/tests/test_versioninfo.py b/lib/lp/scripts/utilities/tests/test_versioninfo.py
index 1c45181..f856298 100644
--- a/lib/lp/scripts/utilities/tests/test_versioninfo.py
+++ b/lib/lp/scripts/utilities/tests/test_versioninfo.py
@@ -3,7 +3,12 @@
 
 """Test the script to show version information."""
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
 
 __metaclass__ = type
 
diff --git a/lib/lp/scripts/utilities/versioninfo.py b/lib/lp/scripts/utilities/versioninfo.py
index ad1b86a..011cba3 100644
--- a/lib/lp/scripts/utilities/versioninfo.py
+++ b/lib/lp/scripts/utilities/versioninfo.py
@@ -6,7 +6,12 @@
 This is useful in deployment scripts.
 """
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
 
 __metaclass__ = type
 __all__ = ['main']
diff --git a/lib/lp/scripts/utilities/warninghandler.py b/lib/lp/scripts/utilities/warninghandler.py
index eb12398..f149062 100644
--- a/lib/lp/scripts/utilities/warninghandler.py
+++ b/lib/lp/scripts/utilities/warninghandler.py
@@ -5,6 +5,7 @@
 
 from __future__ import print_function
 
+
 __metaclass__ = type
 
 import inspect
diff --git a/lib/lp/services/compat.py b/lib/lp/services/compat.py
index 942e690..ca10d0f 100644
--- a/lib/lp/services/compat.py
+++ b/lib/lp/services/compat.py
@@ -6,7 +6,12 @@
 Use this for things that six doesn't provide.
 """
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
 
 __metaclass__ = type
 __all__ = [
diff --git a/lib/lp/services/config/__init__.py b/lib/lp/services/config/__init__.py
index a1b94d9..cd1a548 100644
--- a/lib/lp/services/config/__init__.py
+++ b/lib/lp/services/config/__init__.py
@@ -8,7 +8,11 @@ The configuration section used is specified using the LPCONFIG
 environment variable, and defaults to 'development'
 '''
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 
diff --git a/lib/lp/services/config/tests/test_config.py b/lib/lp/services/config/tests/test_config.py
index 065d4ee..855ad83 100644
--- a/lib/lp/services/config/tests/test_config.py
+++ b/lib/lp/services/config/tests/test_config.py
@@ -24,6 +24,7 @@ import testtools
 import lp.services.config
 from lp.services.config.fixture import ConfigUseFixture
 
+
 # Configs that shouldn't be tested.
 EXCLUDED_CONFIGS = ['lpnet-template']
 
diff --git a/lib/lp/services/log/logger.py b/lib/lp/services/log/logger.py
index 7afe228..0e80e61 100644
--- a/lib/lp/services/log/logger.py
+++ b/lib/lp/services/log/logger.py
@@ -3,7 +3,11 @@
 
 """Loggers."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 __all__ = [
diff --git a/lib/lp/services/log/loglevels.py b/lib/lp/services/log/loglevels.py
index 71d4a70..fca11d1 100644
--- a/lib/lp/services/log/loglevels.py
+++ b/lib/lp/services/log/loglevels.py
@@ -28,6 +28,7 @@ __all__ = [
 
 import logging
 
+
 # Reexport standard log levels.
 DEBUG = logging.DEBUG
 INFO = logging.INFO
diff --git a/lib/lp/services/messaging/tests/test_rabbit.py b/lib/lp/services/messaging/tests/test_rabbit.py
index 6a6ef3e..b25c073 100644
--- a/lib/lp/services/messaging/tests/test_rabbit.py
+++ b/lib/lp/services/messaging/tests/test_rabbit.py
@@ -41,6 +41,7 @@ from lp.testing.faketransaction import FakeTransaction
 from lp.testing.layers import RabbitMQLayer
 from lp.testing.matchers import Provides
 
+
 # RabbitMQ is not (yet) torn down or reset between tests, so here are sources
 # of distinct names.
 queue_names = ("queue.%d" % num for num in count(1))
diff --git a/lib/lp/services/rabbit/server.py b/lib/lp/services/rabbit/server.py
index b446efd..9cd3434 100644
--- a/lib/lp/services/rabbit/server.py
+++ b/lib/lp/services/rabbit/server.py
@@ -3,7 +3,12 @@
 
 """RabbitMQ server fixture."""
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
 
 __metaclass__ = type
 __all__ = [
diff --git a/lib/lp/services/rabbit/tests/test_server.py b/lib/lp/services/rabbit/tests/test_server.py
index fc56263..ed93e2d 100644
--- a/lib/lp/services/rabbit/tests/test_server.py
+++ b/lib/lp/services/rabbit/tests/test_server.py
@@ -3,7 +3,12 @@
 
 """Tests for lp.services.rabbit.RabbitServer."""
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
 
 __metaclass__ = type
 
diff --git a/lib/lp/services/scripts/logger.py b/lib/lp/services/scripts/logger.py
index d687498..dfb4dc0 100644
--- a/lib/lp/services/scripts/logger.py
+++ b/lib/lp/services/scripts/logger.py
@@ -46,6 +46,7 @@ from lp.services.webapp.errorlog import (
     ScriptRequest,
     )
 
+
 # Reexport our custom loglevels for old callsites. These callsites
 # should be importing the symbols from lp.services.log.loglevels
 DEBUG2 = loglevels.DEBUG2
diff --git a/lib/lp/services/scripts/tests/loglevels.py b/lib/lp/services/scripts/tests/loglevels.py
index aaf24ef..0fb5d70 100644
--- a/lib/lp/services/scripts/tests/loglevels.py
+++ b/lib/lp/services/scripts/tests/loglevels.py
@@ -6,7 +6,11 @@
 Used by test_logger.txt.
 """
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 __all__ = []
diff --git a/lib/lp/services/testing/profiled.py b/lib/lp/services/testing/profiled.py
index 1ab83b8..ac31fbc 100644
--- a/lib/lp/services/testing/profiled.py
+++ b/lib/lp/services/testing/profiled.py
@@ -3,7 +3,11 @@
 
 """Profile the test layers."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 __all__ = ['profiled', 'setup_profiling']
diff --git a/lib/lp/services/xmlrpc.py b/lib/lp/services/xmlrpc.py
index 2de38a0..2f33fa2 100644
--- a/lib/lp/services/xmlrpc.py
+++ b/lib/lp/services/xmlrpc.py
@@ -11,6 +11,7 @@ __all__ = [
 from defusedxml.xmlrpc import monkey_patch
 from six.moves import xmlrpc_client
 
+
 # Protect against various XML parsing vulnerabilities.
 monkey_patch()
 
diff --git a/lib/lp/testing/__init__.py b/lib/lp/testing/__init__.py
index 4a315c4..68b2fcc 100644
--- a/lib/lp/testing/__init__.py
+++ b/lib/lp/testing/__init__.py
@@ -1,7 +1,10 @@
 # Copyright 2009-2020 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
 
 
 __metaclass__ = type
@@ -70,8 +73,8 @@ def reset_logging():
     logging._handlers.clear()
 
     # Reset the setup
-    from zope.testrunner.runner import Runner
     from zope.testrunner.logsupport import Logging
+    from zope.testrunner.runner import Runner
     Logging(Runner()).global_setup()
     lp_sitecustomize.customize_logger()
 
diff --git a/lib/lp/testing/faketransaction.py b/lib/lp/testing/faketransaction.py
index 1c41916..ac0a9e2 100644
--- a/lib/lp/testing/faketransaction.py
+++ b/lib/lp/testing/faketransaction.py
@@ -3,7 +3,12 @@
 
 """Fake transaction manager."""
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
 
 __metaclass__ = type
 __all__ = ['FakeTransaction']
diff --git a/lib/lp/testing/layers.py b/lib/lp/testing/layers.py
index 117e3a8..3dd0053 100644
--- a/lib/lp/testing/layers.py
+++ b/lib/lp/testing/layers.py
@@ -18,7 +18,11 @@ of one, forcing us to attempt to make some sort of layer tree.
 -- StuartBishop 20060619
 """
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 __all__ = [
diff --git a/lib/lp/testing/systemdocs.py b/lib/lp/testing/systemdocs.py
index 01a4551..1bbc40a 100644
--- a/lib/lp/testing/systemdocs.py
+++ b/lib/lp/testing/systemdocs.py
@@ -3,7 +3,11 @@
 
 """Infrastructure for setting up doctests."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 __all__ = [
diff --git a/lib/lp/testing/tests/test_layers_functional.py b/lib/lp/testing/tests/test_layers_functional.py
index 8a19b97..b891061 100644
--- a/lib/lp/testing/tests/test_layers_functional.py
+++ b/lib/lp/testing/tests/test_layers_functional.py
@@ -3,6 +3,7 @@
 
 from __future__ import with_statement
 
+
 """ Test layers
 
 Note that many tests are performed at run time in the layers themselves
diff --git a/lib/lp/testing/tests/test_standard_test_template.py b/lib/lp/testing/tests/test_standard_test_template.py
index afdf545..dfe7a0b 100644
--- a/lib/lp/testing/tests/test_standard_test_template.py
+++ b/lib/lp/testing/tests/test_standard_test_template.py
@@ -3,7 +3,12 @@
 
 """XXX: Module docstring goes here."""
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
 
 __metaclass__ = type
 
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..140b14b
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,73 @@
+[flake8]
+# These Python 2 builtins are needed until such time as we finish moving to
+# Python 3.
+builtins =
+    file,
+    long
+exclude =
+    # Code here is imported from elsewhere and may not necessarily conform
+    # to Launchpad's style.
+    lib/contrib
+hang-closing = true
+ignore =
+    # Skip all the pure whitespace issues for now.  There are too many of
+    # them to be worth fixing manually, and most of them will get sorted out
+    # automatically when we eventually switch to black.
+    E111,
+    E114,
+    E115,
+    E116,
+    E117,
+    E121,
+    E122,
+    E124,
+    E125,
+    E126,
+    E127,
+    E128,
+    E129,
+    E131,
+    E133,
+    E201,
+    E202,
+    E211,
+    E221,
+    E222,
+    E225,
+    E226,
+    E227,
+    E231,
+    E241,
+    E251,
+    E261,
+    E262,
+    E265,
+    E266,
+    E271,
+    E302,
+    E303,
+    E305,
+    E306,
+    # ==/!= comparisons with True/False/None are common idioms with Storm.
+    E711,
+    E712,
+    # It's not obvious that forbidding assigning lambdas makes the affected
+    # code much clearer.
+    E731,
+    # Don't enforce either position of line breaks relative to binary
+    # operators, at least for now.
+    W503,
+    W504
+
+[isort]
+combine_as_imports = true
+force_grid_wrap = 2
+force_sort_within_sections = true
+include_trailing_comma = true
+known_first_party = canonical,lp,devscripts
+known_pythonpath = _pythonpath
+line_length = 78
+lines_after_imports = 2
+multi_line_output = 8
+order_by_type = false
+sections = FUTURE,PYTHONPATH,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
diff --git a/standard_template.py b/standard_template.py
index f07b58a..3d117be 100644
--- a/standard_template.py
+++ b/standard_template.py
@@ -3,7 +3,12 @@
 
 """XXX: Module docstring goes here."""
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
 
 __metaclass__ = type
 __all__ = []
diff --git a/utilities/format-imports b/utilities/format-imports
index 084e527..dc6d3bc 100755
--- a/utilities/format-imports
+++ b/utilities/format-imports
@@ -125,7 +125,11 @@ is over the length limit.
 }}}
 """
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metaclass__ = type
 
@@ -135,9 +139,11 @@ import re
 import sys
 from textwrap import dedent
 
+
 sys.path[0:0] = [os.path.dirname(__file__)]
 from python_standard_libs import python_standard_libs  # noqa: E402
 
+
 # python_standard_libs is only used for membership tests.
 python_standard_libs = frozenset(python_standard_libs)
 
diff --git a/utilities/link-system-packages.py b/utilities/link-system-packages.py
index 56e9b95..7ec75f1 100755
--- a/utilities/link-system-packages.py
+++ b/utilities/link-system-packages.py
@@ -5,7 +5,11 @@
 
 """Link system-installed Python modules into Launchpad's virtualenv."""
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
 
 from argparse import ArgumentParser
 from distutils.sysconfig import get_python_lib
diff --git a/utilities/lsconf.py b/utilities/lsconf.py
index aa24349..9f20c50 100755
--- a/utilities/lsconf.py
+++ b/utilities/lsconf.py
@@ -5,7 +5,11 @@
 
 """Create lazr.config schema and confs from ZConfig data."""
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
+
 
 __metatype__ = type
 
diff --git a/utilities/make-requirements.py b/utilities/make-requirements.py
index 92fce84..1f784e0 100755
--- a/utilities/make-requirements.py
+++ b/utilities/make-requirements.py
@@ -5,16 +5,23 @@
 
 """Build a pip constraints file from inputs."""
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
 
 from argparse import ArgumentParser
 from collections import defaultdict
+
+
 try:
     # Python 3
     from configparser import ConfigParser
 except ImportError:
     # Python 2
     from ConfigParser import SafeConfigParser as ConfigParser
+
 import logging
 
 from pkg_resources import parse_requirements
diff --git a/utilities/python_standard_libs.py b/utilities/python_standard_libs.py
index eb76673..dec0085 100644
--- a/utilities/python_standard_libs.py
+++ b/utilities/python_standard_libs.py
@@ -13,8 +13,12 @@ updated from time to time.
 
 # Run this to generate a new module list.
 if __name__ == '__main__':
+    from sys import (
+        stdout,
+        version_info,
+        )
+
     from lxml import html
-    from sys import version_info, stdout
     modindex_url = (
         "http://docs.python.org/release/";
         "{0}.{1}.{2}/modindex.html").format(*version_info)
diff --git a/utilities/run-as b/utilities/run-as
index 27c14b5..9df9447 100755
--- a/utilities/run-as
+++ b/utilities/run-as
@@ -10,7 +10,11 @@ inside "lxc exec".  (sudo in xenial breaks without a tty, so cannot be used
 here.)
 """
 
-from __future__ import absolute_import, print_function, unicode_literals
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
 
 import os
 import pwd
diff --git a/utilities/update-copyright b/utilities/update-copyright
index ee1fad7..3d86539 100755
--- a/utilities/update-copyright
+++ b/utilities/update-copyright
@@ -10,7 +10,10 @@ notice to reflect the current year. Looks for the notice in the first three
 lines of the file and leaves the file unchanged if it finds none.
 """
 
-from __future__ import absolute_import, print_function
+from __future__ import (
+    absolute_import,
+    print_function,
+    )
 
 from datetime import date
 import os
@@ -21,6 +24,7 @@ from subprocess import (
     )
 import sys
 
+
 # This script lives in the 'utilites' directory.
 UTILITIES_DIR = os.path.dirname(__file__)
 CURRENT_YEAR = date.today().year