From bc0ae24da246c781227eafa4aae45b4d77d3f64b Mon Sep 17 00:00:00 2001
From: bretello <bretello@distruzione.org>
Date: Sun, 29 Oct 2023 14:12:09 +0100
Subject: [PATCH 1/9] gha: improvements, drop python versions below 3.8

---
 .github/workflows/ci.yml | 83 +++++++++++-----------------------------
 .travis.yml              | 31 ---------------
 2 files changed, 22 insertions(+), 92 deletions(-)
 delete mode 100644 .travis.yml

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 76e8e84..36c2af4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -21,92 +21,53 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        include:
-          # Linux
-          - tox_env: "py39-coverage"
-            python: "3.9"
-            os: ubuntu-20.04
-          - tox_env: "py38-coverage"
-            python: "3.8"
-            os: ubuntu-20.04
-          - tox_env: "py37-coverage"
-            python: "3.7"
-            os: ubuntu-20.04
-          - tox_env: "py36-coverage"
-            python: "3.6"
-            os: ubuntu-20.04
-          - tox_env: "py35-coverage"
-            python: "3.5"
-            os: ubuntu-20.04
-
-          - tox_env: "py27-coverage"
-            python: "2.7"
-            os: ubuntu-20.04
-
-          - tox_env: "pypy3-coverage"
-            python: "pypy-3.7"
-            os: ubuntu-20.04
-          - tox_env: "pypy-coverage"
-            python: "pypy-2.7"
-            os: ubuntu-20.04
+        os: [ubuntu-latest]
+        python: ["3.8", "3.9", "3.10", "3.11"]
 
     steps:
-      - uses: actions/checkout@v2
-        with:
-          fetch-depth: 0
+      - uses: actions/checkout@v4
       - name: Set up Python ${{ matrix.python }}
-        uses: actions/setup-python@v2
+        uses: actions/setup-python@v4
         with:
           python-version: ${{ matrix.python }}
 
-      # Caching.
+      - name: Set tox_env
+        run: |
+          pyv=$(echo ${{matrix.python}} | sed 's/\.//')
+          tox_env=py${pyv}-coverage
+          echo "tox env name: ${tox_env}"
+          echo "tox_env=${tox_env}">> $GITHUB_ENV
+
       - name: set PY_CACHE_KEY
         run: echo "PY_CACHE_KEY=$(python -c 'import hashlib, sys;print(hashlib.sha256(sys.version.encode()+sys.executable.encode()).hexdigest())')" >> $GITHUB_ENV
       - name: Cache .tox
-        uses: actions/cache@v1
+        uses: actions/cache@v3
         with:
-          path: ${{ github.workspace }}/.tox/${{ matrix.tox_env }}
-          key: "tox|${{ matrix.os }}|${{ matrix.tox_env }}|${{ env.PY_CACHE_KEY }}|${{ hashFiles('tox.ini', 'setup.*') }}"
+          path: ${{ github.workspace }}/.tox/${{ env.tox_env }}
+          key: "tox|${{ matrix.os }}|${{ env.tox_env }}|${{ env.PY_CACHE_KEY }}|${{ hashFiles('tox.ini', 'setup.*') }}"
 
-      - name: (Initial) version information/pinning
+      - name: Install/update tools
         run: |
-          set -x
-          python -m site
-          python -m pip --version
-          python -m pip list
-          if [[ "${{ matrix.python }}" == "3.4" ]]; then
-            # Install latest available pip.
-            # 7.1.2 (installed) is too old to not install too new packages,
-            # including pip itself.  19.2 dropped support for Python 3.4.
-            python -m pip install -U pip==19.1.1
-          fi
-          python -m pip install -U setuptools==42.0.2
-          python -m pip install -U virtualenv==20.4.3
-
-      - name: Install tox
-        run: python -m pip install git+https://github.com/blueyed/tox@master
-
-      - name: Version information
-        run: python -m pip list
+          pip install -U pip setuptools virtualenv tox
 
       - name: Setup tox environment
         id: setup-tox
-        run: python -m tox --notest -v --durations -e ${{ matrix.tox_env }}
+        run: tox --notest -v -e ${{ env.tox_env }}
 
       - name: Test
         env:
-          COLUMNS: "90"  # better alignment (working around https://github.com/blueyed/pytest/issues/491).
+          COLUMNS: "90" # better alignment (working around https://github.com/blueyed/pytest/issues/491).
           PY_COLORS: "1"
           # UTF-8 mode for Windows (https://docs.python.org/3/using/windows.html#utf-8-mode).
           PYTHONUTF8: "1"
           TOX_TESTENV_PASSENV: "PYTHONUTF8"
-        run: python -m tox -v --durations -e ${{ matrix.tox_env }}
+        run: tox -v -e ${{ env.tox_env }}
 
       - name: Report coverage
-        if: always() && (steps.setup-tox.outcome == 'success' && contains(matrix.tox_env, '-coverage'))
-        uses: codecov/codecov-action@v1
+        if: always() && (steps.setup-tox.outcome == 'success' && contains(env.tox_env, '-coverage'))
+        uses: codecov/codecov-action@v3
         with:
           files: ./coverage.xml
           flags: ${{ runner.os }}
-          name: ${{ matrix.tox_env }}
+          name: ${{ env.tox_env }}
           fail_ci_if_error: true
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 7ba9ffc..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-dist: xenial
-language: python
-
-env:
-  global:
-    - PYTEST_ADDOPTS="-vv"
-
-jobs:
-  include:
-    - python: '3.4'
-      env:
-        - TOXENV=py34-coverage
-        - PYTEST="pytest @ git+https://github.com/blueyed/pytest@my-4.6-maintenance"
-
-install:
-  - pip install tox
-
-script:
-  - tox --force-dep="$PYTEST"
-
-after_script:
-  - |
-    if [[ "${TOXENV%-coverage}" != "$TOXENV" ]]; then
-      bash <(curl -s https://codecov.io/bash) -Z -X gcov -X coveragepy -X search -X xcode -X gcovout -X fix -f coverage.xml -n $TOXENV
-    fi
-
-# Only master and releases.  PRs are used otherwise.
-branches:
-  only:
-    - master
-    - /^\d+\.\d+(\.\d+)?(-\S*)?$/

From d3bd64f060a8a169963edd76c4cdf6b11bf41bdc Mon Sep 17 00:00:00 2001
From: bretello <bretello@distruzione.org>
Date: Sun, 29 Oct 2023 14:12:45 +0100
Subject: [PATCH 2/9] gha: add release workflow

---
 .github/workflows/release.yml | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 .github/workflows/release.yml

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..0894441
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,28 @@
+name: Publish package to PyPI
+
+on:
+  release:
+    types:
+      - published
+  workflow_dispatch:
+
+jobs:
+  deploy:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - uses: actions/setup-python@v4
+        with:
+          python-version: "3.9"
+      - name: Install requirements
+        run: |
+          pip install -U pip twine build
+      - name: Build
+        run: python -m build
+      - run: check-manifest
+      - run: twine check dist/*
+      - name: Publish to PyPI
+        env:
+          TWINE_USERNAME: "__token__"
+          TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
+        run: twine upload dist/*

From f942e10a8636aedd47d03f3b7243d02a9eb16a9b Mon Sep 17 00:00:00 2001
From: bretello <bretello@distruzione.org>
Date: Sun, 29 Oct 2023 14:18:57 +0100
Subject: [PATCH 3/9] mv testing tests

---
 {testing => tests}/__init__.py         | 0
 {testing => tests}/infrastructure.py   | 0
 {testing => tests}/test_basic.py       | 0
 {testing => tests}/test_bugs.py        | 0
 {testing => tests}/test_curses.py      | 0
 {testing => tests}/test_functional.py  | 0
 {testing => tests}/test_keymap.py      | 0
 {testing => tests}/test_readline.py    | 0
 {testing => tests}/test_unix_reader.py | 0
 {testing => tests}/test_wishes.py      | 0
 tox.ini                                | 6 +++---
 11 files changed, 3 insertions(+), 3 deletions(-)
 rename {testing => tests}/__init__.py (100%)
 rename {testing => tests}/infrastructure.py (100%)
 rename {testing => tests}/test_basic.py (100%)
 rename {testing => tests}/test_bugs.py (100%)
 rename {testing => tests}/test_curses.py (100%)
 rename {testing => tests}/test_functional.py (100%)
 rename {testing => tests}/test_keymap.py (100%)
 rename {testing => tests}/test_readline.py (100%)
 rename {testing => tests}/test_unix_reader.py (100%)
 rename {testing => tests}/test_wishes.py (100%)

diff --git a/testing/__init__.py b/tests/__init__.py
similarity index 100%
rename from testing/__init__.py
rename to tests/__init__.py
diff --git a/testing/infrastructure.py b/tests/infrastructure.py
similarity index 100%
rename from testing/infrastructure.py
rename to tests/infrastructure.py
diff --git a/testing/test_basic.py b/tests/test_basic.py
similarity index 100%
rename from testing/test_basic.py
rename to tests/test_basic.py
diff --git a/testing/test_bugs.py b/tests/test_bugs.py
similarity index 100%
rename from testing/test_bugs.py
rename to tests/test_bugs.py
diff --git a/testing/test_curses.py b/tests/test_curses.py
similarity index 100%
rename from testing/test_curses.py
rename to tests/test_curses.py
diff --git a/testing/test_functional.py b/tests/test_functional.py
similarity index 100%
rename from testing/test_functional.py
rename to tests/test_functional.py
diff --git a/testing/test_keymap.py b/tests/test_keymap.py
similarity index 100%
rename from testing/test_keymap.py
rename to tests/test_keymap.py
diff --git a/testing/test_readline.py b/tests/test_readline.py
similarity index 100%
rename from testing/test_readline.py
rename to tests/test_readline.py
diff --git a/testing/test_unix_reader.py b/tests/test_unix_reader.py
similarity index 100%
rename from testing/test_unix_reader.py
rename to tests/test_unix_reader.py
diff --git a/testing/test_wishes.py b/tests/test_wishes.py
similarity index 100%
rename from testing/test_wishes.py
rename to tests/test_wishes.py
diff --git a/tox.ini b/tox.ini
index 67b30a5..cc5c970 100644
--- a/tox.ini
+++ b/tox.ini
@@ -21,16 +21,16 @@ setenv =
 deps =
     flake8
     mccabe
-commands = flake8 --max-complexity=10 setup.py pyrepl testing pythoni pythoni1
+commands = flake8 --max-complexity=10 setup.py pyrepl tests pythoni pythoni1
 
 [pytest]
-testpaths = testing
+testpaths = tests
 addopts = -ra
 filterwarnings =
     error
 
 [coverage:run]
-include = pyrepl/*, testing/*
+include = pyrepl/*, tests/*
 parallel = 1
 branch = 1
 

From 340f59749354db97f64d2cff516e65bb0d296df0 Mon Sep 17 00:00:00 2001
From: bretello <bretello@distruzione.org>
Date: Sun, 29 Oct 2023 14:24:51 +0100
Subject: [PATCH 4/9] add gitignore

---
 .gitignore | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 .gitignore

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a790198
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,56 @@
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+*.manifest
+*.spec
+pip-log.txt
+pip-delete-this-directory.txt
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+*.mo
+*.pot
+*.log
+
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+.ruff_cache/

From ef2d19aa9b056ea5066d703489e840fccb596f26 Mon Sep 17 00:00:00 2001
From: bretello <bretello@distruzione.org>
Date: Sun, 29 Oct 2023 14:36:22 +0100
Subject: [PATCH 5/9] run 2to3

---
 pyrepl/cmdrepl.py           |  2 +-
 pyrepl/completer.py         |  6 ++--
 pyrepl/completing_reader.py |  2 +-
 pyrepl/historical_reader.py |  4 +--
 pyrepl/input.py             |  2 +-
 pyrepl/keymap.py            |  6 ++--
 pyrepl/keymaps.py           |  8 ++---
 pyrepl/pygame_console.py    | 17 +++++-----
 pyrepl/pygame_keymap.py     | 62 +++++++++++++++++--------------------
 pyrepl/python_reader.py     | 12 +++----
 pyrepl/reader.py            | 44 +++++++++++++-------------
 pyrepl/readline.py          | 26 ++++++++--------
 pyrepl/unix_console.py      | 10 +++---
 pyrepl/unix_eventqueue.py   |  8 ++---
 tests/infrastructure.py     |  2 +-
 tests/test_functional.py    |  2 +-
 tests/test_readline.py      |  2 +-
 tests/test_unix_reader.py   |  4 +--
 18 files changed, 107 insertions(+), 112 deletions(-)

diff --git a/pyrepl/cmdrepl.py b/pyrepl/cmdrepl.py
index 8b500b0..9bd1fe0 100644
--- a/pyrepl/cmdrepl.py
+++ b/pyrepl/cmdrepl.py
@@ -33,7 +33,7 @@
 which is in fact done by the `pythoni' script that comes with
 pyrepl."""
 
-from __future__ import print_function
+
 
 from pyrepl import completer
 from pyrepl.completing_reader import CompletingReader as CR
diff --git a/pyrepl/completer.py b/pyrepl/completer.py
index 45f40c1..d44f4b9 100644
--- a/pyrepl/completer.py
+++ b/pyrepl/completer.py
@@ -18,7 +18,7 @@
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 try:
-    import __builtin__ as builtins
+    import builtins as builtins
     builtins  # silence broken pyflakes
 except ImportError:
     import builtins
@@ -44,8 +44,8 @@ def global_matches(self, text):
         import keyword
         matches = []
         for list in [keyword.kwlist,
-                     builtins.__dict__.keys(),
-                     self.ns.keys()]:
+                     list(builtins.__dict__.keys()),
+                     list(self.ns.keys())]:
             for word in list:
                 if word.startswith(text) and word != "__builtins__":
                     matches.append(word)
diff --git a/pyrepl/completing_reader.py b/pyrepl/completing_reader.py
index 6039688..c7d0a7a 100644
--- a/pyrepl/completing_reader.py
+++ b/pyrepl/completing_reader.py
@@ -64,7 +64,7 @@ def build_menu(cons, wordlist, start, use_brackets, sort_in_column):
     else:
         item = "%s  "
         padding = 2
-    maxlen = min(max(map(real_len, wordlist)), cons.width - padding)
+    maxlen = min(max(list(map(real_len, wordlist))), cons.width - padding)
     cols = int(cons.width / (maxlen + padding))
     rows = int((len(wordlist) - 1)/cols + 1)
 
diff --git a/pyrepl/historical_reader.py b/pyrepl/historical_reader.py
index 01522ee..d21ddb7 100644
--- a/pyrepl/historical_reader.py
+++ b/pyrepl/historical_reader.py
@@ -23,7 +23,7 @@
 isearch_keymap = tuple(
     [('\\%03o'%c, 'isearch-end') for c in range(256) if chr(c) != '\\'] + \
     [(c, 'isearch-add-character')
-     for c in map(chr, range(32, 127)) if c != '\\'] + \
+     for c in map(chr, list(range(32, 127))) if c != '\\'] + \
     [('\\%03o'%c, 'isearch-add-character')
      for c in range(256) if chr(c).isalpha() and chr(c) != '\\'] + \
     [('\\\\', 'self-insert'),
@@ -292,7 +292,7 @@ def isearch_next(self):
     def finish(self):
         super(HistoricalReader, self).finish()
         ret = self.get_unicode()
-        for i, t in self.transient_history.items():
+        for i, t in list(self.transient_history.items()):
             if i < len(self.history) and i != self.historyi:
                 self.history[i] = t
         if ret:
diff --git a/pyrepl/input.py b/pyrepl/input.py
index e92adbd..36c0a1d 100644
--- a/pyrepl/input.py
+++ b/pyrepl/input.py
@@ -32,7 +32,7 @@
 # executive, temporary decision: [tab] and [C-i] are distinct, but
 # [meta-key] is identified with [esc key].  We demand that any console
 # class does quite a lot towards emulating a unix terminal.
-from __future__ import print_function
+
 import unicodedata
 from collections import deque
 import pprint
diff --git a/pyrepl/keymap.py b/pyrepl/keymap.py
index 2fcfce3..830675e 100644
--- a/pyrepl/keymap.py
+++ b/pyrepl/keymap.py
@@ -174,17 +174,17 @@ def parse_keys(key):
 
 def compile_keymap(keymap, empty=b''):
     r = {}
-    for key, value in keymap.items():
+    for key, value in list(keymap.items()):
         if isinstance(key, bytes):
             first = key[:1]
         else:
             first = key[0]
         r.setdefault(first, {})[key[1:]] = value
-    for key, value in r.items():
+    for key, value in list(r.items()):
         if empty in value:
             if len(value) != 1:
                 raise KeySpecError(
-                      "key definitions for %s clash"%(value.values(),))
+                      "key definitions for %s clash"%(list(value.values()),))
             else:
                 r[key] = value[empty]
         else:
diff --git a/pyrepl/keymaps.py b/pyrepl/keymaps.py
index 76ba896..c25207c 100644
--- a/pyrepl/keymaps.py
+++ b/pyrepl/keymaps.py
@@ -62,9 +62,9 @@
      (r'\M-\n', 'self-insert'),
      (r'\<backslash>', 'self-insert')] + \
     [(c, 'self-insert')
-     for c in map(chr, range(32, 127)) if c <> '\\'] + \
+     for c in map(chr, list(range(32, 127))) if c != '\\'] + \
     [(c, 'self-insert')
-     for c in map(chr, range(128, 256)) if c.isalpha()] + \
+     for c in map(chr, list(range(128, 256))) if c.isalpha()] + \
     [(r'\<up>', 'up'),
      (r'\<down>', 'down'),
      (r'\<left>', 'left'),
@@ -101,9 +101,9 @@
     
 reader_vi_insert_keymap = tuple(
     [(c, 'self-insert')
-     for c in map(chr, range(32, 127)) if c <> '\\'] + \
+     for c in map(chr, list(range(32, 127))) if c != '\\'] + \
     [(c, 'self-insert')
-     for c in map(chr, range(128, 256)) if c.isalpha()] + \
+     for c in map(chr, list(range(128, 256))) if c.isalpha()] + \
     [(r'\C-d', 'delete'),
      (r'\<backspace>', 'backspace'),
      ('')])
diff --git a/pyrepl/pygame_console.py b/pyrepl/pygame_console.py
index cb90b8b..4eedf50 100644
--- a/pyrepl/pygame_console.py
+++ b/pyrepl/pygame_console.py
@@ -72,7 +72,7 @@ def read(self, n=None):
         # argh!
         raise NotImplementedError
     def readline(self, n=None):
-        from reader import Reader
+        from .reader import Reader
         try:
             # this isn't quite right: it will clobber any prompt that's
             # been printed.  Not sure how to get around this...
@@ -130,7 +130,8 @@ def paint_margin(self):
         s.fill(c, [0, 600 - bmargin, 800, bmargin])
         s.fill(c, [800 - rmargin, 0, lmargin, 600])
 
-    def refresh(self, screen, (cx, cy)):
+    def refresh(self, screen, xxx_todo_changeme):
+        (cx, cy) = xxx_todo_changeme
         self.screen = screen
         self.pygame_screen.fill(colors.bg,
                                 [0, tmargin + self.cur_top + self.scroll,
@@ -211,12 +212,12 @@ def tr_event(self, pyg_event):
         meta = bool(pyg_event.mod & (KMOD_ALT|KMOD_META))
 
         try:
-            return self.k[(pyg_event.unicode, meta, ctrl)], pyg_event.unicode
+            return self.k[(pyg_event.str, meta, ctrl)], pyg_event.str
         except KeyError:
             try:
-                return self.k[(pyg_event.key, meta, ctrl)], pyg_event.unicode
+                return self.k[(pyg_event.key, meta, ctrl)], pyg_event.str
             except KeyError:
-                return "invalid-key", pyg_event.unicode
+                return "invalid-key", pyg_event.str
 
     def get_event(self, block=1):
         """Return an Event instance.  Returns None if |block| is false
@@ -239,7 +240,7 @@ def get_event(self, block=1):
             self.cmd_buf += c.encode('ascii', 'replace')
             self.k = k
 
-            if not isinstance(k, types.DictType):
+            if not isinstance(k, dict):
                 e = Event(k, self.cmd_buf, [])
                 self.k = self.keymap
                 self.cmd_buf = ''
@@ -282,7 +283,7 @@ def flushoutput(self):
 
     def forgetinput(self):
         """Forget all pending, but not yet processed input."""
-        while pygame.event.poll().type <> NOEVENT:
+        while pygame.event.poll().type != NOEVENT:
             pass
     
     def getpending(self):
@@ -299,7 +300,7 @@ def getpending(self):
 
     def wait(self):
         """Wait for an event."""
-        raise Exception, "erp!"
+        raise Exception("erp!")
 
     def repaint(self):
         # perhaps we should consolidate grobs?
diff --git a/pyrepl/pygame_keymap.py b/pyrepl/pygame_keymap.py
index 5531f1c..b4018e4 100644
--- a/pyrepl/pygame_keymap.py
+++ b/pyrepl/pygame_keymap.py
@@ -85,27 +85,25 @@ def _parse_key1(key, s):
     while not ret and s < len(key):
         if key[s] == '\\':
             c = key[s+1].lower()
-            if _escapes.has_key(c):
+            if c in _escapes:
                 ret = _escapes[c]
                 s += 2
             elif c == "c":
                 if key[s + 2] != '-':
-                    raise KeySpecError, \
-                              "\\C must be followed by `-' (char %d of %s)"%(
-                        s + 2, repr(key))
+                    raise KeySpecError("\\C must be followed by `-' (char %d of %s)"%(
+                        s + 2, repr(key)))
                 if ctrl:
-                    raise KeySpecError, "doubled \\C- (char %d of %s)"%(
-                        s + 1, repr(key))
+                    raise KeySpecError("doubled \\C- (char %d of %s)"%(
+                        s + 1, repr(key)))
                 ctrl = 1
                 s += 3
             elif c == "m":
                 if key[s + 2] != '-':
-                    raise KeySpecError, \
-                              "\\M must be followed by `-' (char %d of %s)"%(
-                        s + 2, repr(key))
+                    raise KeySpecError("\\M must be followed by `-' (char %d of %s)"%(
+                        s + 2, repr(key)))
                 if meta:
-                    raise KeySpecError, "doubled \\M- (char %d of %s)"%(
-                        s + 1, repr(key))
+                    raise KeySpecError("doubled \\M- (char %d of %s)"%(
+                        s + 1, repr(key)))
                 meta = 1
                 s += 3
             elif c.isdigit():
@@ -119,28 +117,25 @@ def _parse_key1(key, s):
             elif c == '<':
                 t = key.find('>', s)
                 if t == -1:
-                    raise KeySpecError, \
-                              "unterminated \\< starting at char %d of %s"%(
-                        s + 1, repr(key))
+                    raise KeySpecError("unterminated \\< starting at char %d of %s"%(
+                        s + 1, repr(key)))
                 try:
                     ret = _keynames[key[s+2:t].lower()]
                     s = t + 1
                 except KeyError:
-                    raise KeySpecError, \
-                              "unrecognised keyname `%s' at char %d of %s"%(
-                        key[s+2:t], s + 2, repr(key))
+                    raise KeySpecError("unrecognised keyname `%s' at char %d of %s"%(
+                        key[s+2:t], s + 2, repr(key)))
                 if ret is None:
                     return None, s
             else:
-                raise KeySpecError, \
-                          "unknown backslash escape %s at char %d of %s"%(
-                    `c`, s + 2, repr(key))
+                raise KeySpecError("unknown backslash escape %s at char %d of %s"%(
+                    repr(c), s + 2, repr(key)))
         else:
             if ctrl:
                 ret = chr(ord(key[s]) & 0x1f)   # curses.ascii.ctrl()
-                ret = unicode(ret)
+                ret = str(ret)
             else:
-                ret = unicode(key[s])
+                ret = str(key[s])
             s += 1
     return (ret, meta, ctrl), s
 
@@ -156,13 +151,12 @@ def parse_keys(key):
 
 def _compile_keymap(keymap):
     r = {}
-    for key, value in keymap.items():
+    for key, value in list(keymap.items()):
         r.setdefault(key[0], {})[key[1:]] = value
-    for key, value in r.items():
-        if value.has_key(()):
-            if len(value) <> 1:
-                raise KeySpecError, \
-                          "key definitions for %s clash"%(value.values(),)
+    for key, value in list(r.items()):
+        if () in value:
+            if len(value) != 1:
+                raise KeySpecError("key definitions for %s clash"%(list(value.values()),))
             else:
                 r[key] = value[()]
         else:
@@ -173,7 +167,7 @@ def compile_keymap(keymap):
     r = {}
     for key, value in keymap:
         k = parse_keys(key)
-        if value is None and r.has_key(k):
+        if value is None and k in r:
             del r[k]
         if k is not None:
             r[k] = value
@@ -182,7 +176,7 @@ def compile_keymap(keymap):
 def keyname(key):
     longest_match = ''
     longest_match_name = ''
-    for name, keyseq in keyset.items():
+    for name, keyseq in list(keyset.items()):
         if keyseq and key.startswith(keyseq) and \
                len(keyseq) > len(longest_match):
             longest_match = keyseq
@@ -202,7 +196,7 @@ def unparse_key(keyseq):
         return ''
     name, s = keyname(keyseq)
     if name:
-        if name <> 'escape' or s == len(keyseq):
+        if name != 'escape' or s == len(keyseq):
             return '\\<' + name + '>' + unparse_key(keyseq[s:])
         else:
             return '\\M-' + unparse_key(keyseq[1:])
@@ -211,7 +205,7 @@ def unparse_key(keyseq):
         r = keyseq[1:]
         if c == '\\':
             p = '\\\\'
-        elif _unescapes.has_key(c):
+        elif c in _unescapes:
             p = _unescapes[c]
         elif ord(c) < ord(' '):
             p = '\\C-%s'%(chr(ord(c)+96),)
@@ -226,7 +220,7 @@ def _unparse_keyf(keyseq):
         return []
     name, s = keyname(keyseq)
     if name:
-        if name <> 'escape' or s == len(keyseq):
+        if name != 'escape' or s == len(keyseq):
             return [name] + _unparse_keyf(keyseq[s:])
         else:
             rest = _unparse_keyf(keyseq[1:])
@@ -236,7 +230,7 @@ def _unparse_keyf(keyseq):
         r = keyseq[1:]
         if c == '\\':
             p = '\\'
-        elif _unescapes.has_key(c):
+        elif c in _unescapes:
             p = _unescapes[c]
         elif ord(c) < ord(' '):
             p = 'C-%s'%(chr(ord(c)+96),)
diff --git a/pyrepl/python_reader.py b/pyrepl/python_reader.py
index 5e2d4e7..f3a688e 100644
--- a/pyrepl/python_reader.py
+++ b/pyrepl/python_reader.py
@@ -20,8 +20,8 @@
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 # one impressive collections of imports:
-from __future__ import print_function
-from __future__ import unicode_literals
+
+
 from pyrepl.completing_reader import CompletingReader
 from pyrepl.historical_reader import HistoricalReader
 from pyrepl import completing_reader, reader
@@ -31,9 +31,9 @@
 import atexit, warnings
 
 try:
-    unicode
+    str
 except:
-    unicode = str
+    str = str
 
 try:
     imp.find_module("twisted")
@@ -204,7 +204,7 @@ def interact(self):
                         # can't have warnings spewed onto terminal
                         sv = warnings.showwarning
                         warnings.showwarning = eat_it
-                        l = unicode(self.reader.readline(), 'utf-8')
+                        l = str(self.reader.readline(), 'utf-8')
                     finally:
                         warnings.showwarning = sv
                 except KeyboardInterrupt:
@@ -302,7 +302,7 @@ def tkinteract(self):
                     self.prepare()
                     try:
                         while 1:
-                            if sys.modules.has_key("_tkinter"):
+                            if "_tkinter" in sys.modules:
                                 self.really_tkinteract()
                                 # really_tkinteract is not expected to
                                 # return except via an exception, but:
diff --git a/pyrepl/reader.py b/pyrepl/reader.py
index e1db998..5d4c727 100644
--- a/pyrepl/reader.py
+++ b/pyrepl/reader.py
@@ -19,32 +19,32 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-from __future__ import unicode_literals
+
 import unicodedata
 from pyrepl import commands
 from pyrepl import input
 try:
-    unicode
+    str
 except NameError:
-    unicode = str
-    unichr = chr
-    basestring = bytes, str
+    str = str
+    chr = chr
+    str = bytes, str
 
 
 def _make_unctrl_map():
     uc_map = {}
-    for c in map(unichr, range(256)):
+    for c in map(chr, list(range(256))):
         if unicodedata.category(c)[0] != 'C':
             uc_map[c] = c
     for i in range(32):
-        c = unichr(i)
-        uc_map[c] = '^' + unichr(ord('A') + i - 1)
+        c = chr(i)
+        uc_map[c] = '^' + chr(ord('A') + i - 1)
     uc_map[b'\t'] = '    '  # display TABs as 4 characters
-    uc_map[b'\177'] = unicode('^?')
+    uc_map[b'\177'] = str('^?')
     for i in range(256):
-        c = unichr(i)
+        c = chr(i)
         if c not in uc_map:
-            uc_map[c] = unicode('\\%03o') % i
+            uc_map[c] = str('\\%03o') % i
     return uc_map
 
 
@@ -87,17 +87,17 @@ def disp_str(buffer, join=''.join, uc=_my_unctrl):
 
 [SYNTAX_WHITESPACE,
  SYNTAX_WORD,
- SYNTAX_SYMBOL] = range(3)
+ SYNTAX_SYMBOL] = list(range(3))
 
 
 def make_default_syntax_table():
     # XXX perhaps should use some unicodedata here?
     st = {}
-    for c in map(unichr, range(256)):
+    for c in map(chr, list(range(256))):
         st[c] = SYNTAX_SYMBOL
-    for c in [a for a in map(unichr, range(256)) if a.isalpha()]:
+    for c in [a for a in map(chr, list(range(256))) if a.isalpha()]:
         st[c] = SYNTAX_WORD
-    st[unicode('\n')] = st[unicode(' ')] = SYNTAX_WHITESPACE
+    st[str('\n')] = st[str(' ')] = SYNTAX_WHITESPACE
     return st
 
 default_keymap = tuple(
@@ -145,9 +145,9 @@ def make_default_syntax_table():
      #(r'\M-\n', 'insert-nl'),
      ('\\\\', 'self-insert')] +
     [(c, 'self-insert')
-     for c in map(chr, range(32, 127)) if c != '\\'] +
+     for c in map(chr, list(range(32, 127))) if c != '\\'] +
     [(c, 'self-insert')
-     for c in map(chr, range(128, 256)) if c.isalpha()] +
+     for c in map(chr, list(range(128, 256))) if c.isalpha()] +
     [(r'\<up>', 'up'),
      (r'\<down>', 'down'),
      (r'\<left>', 'left'),
@@ -245,7 +245,7 @@ def __init__(self, console):
         self.console = console
         self.commands = {}
         self.msg = ''
-        for v in vars(commands).values():
+        for v in list(vars(commands).values()):
             if (isinstance(v, type) and
                     issubclass(v, commands.Command) and
                     v.__name__[0].islower()):
@@ -273,7 +273,7 @@ def calc_screen(self):
         screeninfo = []
         w = self.console.width - 1
         p = self.pos
-        for ln, line in zip(range(len(lines)), lines):
+        for ln, line in zip(list(range(len(lines))), lines):
             ll = len(line)
             if 0 <= p <= ll:
                 if self.msg and not self.msg_at_bottom:
@@ -523,7 +523,7 @@ def refresh(self):
 
     def do_cmd(self, cmd):
         #print cmd
-        if isinstance(cmd[0], basestring):
+        if isinstance(cmd[0], str):
             #XXX: unify to text
             cmd = self.commands.get(cmd[0],
                                     commands.invalid_command)(self, *cmd)
@@ -619,11 +619,11 @@ def bind(self, spec, command):
     def get_buffer(self, encoding=None):
         if encoding is None:
             encoding = self.console.encoding
-        return unicode('').join(self.buffer).encode(self.console.encoding)
+        return str('').join(self.buffer).encode(self.console.encoding)
 
     def get_unicode(self):
         """Return the current buffer as a unicode string."""
-        return unicode('').join(self.buffer)
+        return str('').join(self.buffer)
 
 
 def test():
diff --git a/pyrepl/readline.py b/pyrepl/readline.py
index 8ac466d..e9022cc 100644
--- a/pyrepl/readline.py
+++ b/pyrepl/readline.py
@@ -33,13 +33,13 @@
 from pyrepl.completing_reader import CompletingReader
 from pyrepl.unix_console import UnixConsole, _error
 try:
-    unicode
+    str
     PY3 = False
 except NameError:
     PY3 = True
-    unicode = str
-    unichr = chr
-    basestring = bytes, str
+    str = str
+    chr = chr
+    str = bytes, str
 
 
 ENCODING = sys.getfilesystemencoding() or 'latin1'     # XXX review
@@ -232,7 +232,7 @@ def raw_input(self, prompt=''):
 
         # Unicode/str is required for Python 3 (3.5.2).
         # Ref: https://bitbucket.org/pypy/pyrepl/issues/20/#comment-30647029
-        return unicode(ret, ENCODING)
+        return str(ret, ENCODING)
 
     def multiline_input(self, more_lines, ps1, ps2, returns_unicode=False):
         """Read an input on possibly multiple lines, asking for more
@@ -272,9 +272,9 @@ def _histline(self, line):
             return line
 
         try:
-            return unicode(line, ENCODING)
+            return str(line, ENCODING)
         except UnicodeDecodeError:   # bah, silently fall back...
-            return unicode(line, 'utf-8', 'replace')
+            return str(line, 'utf-8', 'replace')
 
     def get_history_length(self):
         return self.saved_history_length
@@ -312,7 +312,7 @@ def write_history_file(self, filename='~/.history'):
         for entry in history:
             # if we are on py3k, we don't need to encode strings before
             # writing it to a file
-            if isinstance(entry, unicode) and sys.version_info < (3,):
+            if isinstance(entry, str) and sys.version_info < (3,):
                 entry = entry.encode('utf-8')
             entry = entry.replace('\n', '\r\n')   # multiline history support
             entries += entry + '\n'
@@ -421,7 +421,7 @@ def _make_stub(_name, _ret):
     def stub(*args, **kwds):
         import warnings
         warnings.warn("readline.%s() not implemented" % _name, stacklevel=2)
-    stub.func_name = _name
+    stub.__name__ = _name
     globals()[_name] = stub
 
 for _name, _ret in [
@@ -463,15 +463,15 @@ def _old_raw_input(prompt=''):
                 del sys.__raw_input__
             except AttributeError:
                 pass
-            return raw_input(prompt)
+            return input(prompt)
         sys.__raw_input__ = _wrapper.raw_input
 
     else:
         # this is not really what readline.c does.  Better than nothing I guess
         try:
-            import __builtin__
-            _old_raw_input = __builtin__.raw_input
-            __builtin__.raw_input = _wrapper.raw_input
+            import builtins
+            _old_raw_input = builtins.raw_input
+            builtins.raw_input = _wrapper.raw_input
         except ImportError:
             import builtins
             _old_raw_input = builtins.input
diff --git a/pyrepl/unix_console.py b/pyrepl/unix_console.py
index 281c200..39fff7c 100644
--- a/pyrepl/unix_console.py
+++ b/pyrepl/unix_console.py
@@ -40,9 +40,9 @@ class InvalidTerminal(RuntimeError):
     pass
 
 try:
-    unicode
+    str
 except NameError:
-    unicode = str
+    str = str
 
 _error = (termios.error, curses.error, InvalidTerminal)
 
@@ -221,7 +221,7 @@ def refresh(self, screen, c_xy):
 
         self.__offset = offset
 
-        for y, oldline, newline, in zip(range(offset, offset + height),
+        for y, oldline, newline, in zip(list(range(offset, offset + height)),
                                         oldscr,
                                         newscr):
             if oldline != newline:
@@ -538,7 +538,7 @@ def getpending(self):
             amount = struct.unpack(
                 "i", ioctl(self.input_fd, FIONREAD, "\0\0\0\0"))[0]
             data = os.read(self.input_fd, amount)
-            raw = unicode(data, self.encoding, 'replace')
+            raw = str(data, self.encoding, 'replace')
             #XXX: something is wrong here
             e.data += raw
             e.raw += raw
@@ -554,7 +554,7 @@ def getpending(self):
 
             amount = 10000
             data = os.read(self.input_fd, amount)
-            raw = unicode(data, self.encoding, 'replace')
+            raw = str(data, self.encoding, 'replace')
             #XXX: something is wrong here
             e.data += raw
             e.raw += raw
diff --git a/pyrepl/unix_eventqueue.py b/pyrepl/unix_eventqueue.py
index 332d952..91dc076 100644
--- a/pyrepl/unix_eventqueue.py
+++ b/pyrepl/unix_eventqueue.py
@@ -30,9 +30,9 @@
 from termios import tcgetattr, VERASE
 import os
 try:
-    unicode
+    str
 except NameError:
-    unicode = str
+    str = str
 
 
 _keynames = {
@@ -74,7 +74,7 @@
 
 def general_keycodes():
     keycodes = {}
-    for key, tiname in _keynames.items():
+    for key, tiname in list(_keynames.items()):
         keycode = curses.tigetstr(tiname)
         trace('key {key} tiname {tiname} keycode {keycode!r}', **locals())
         if keycode:
@@ -87,7 +87,7 @@ def EventQueue(fd, encoding):
     keycodes = general_keycodes()
     if os.isatty(fd):
         backspace = tcgetattr(fd)[6][VERASE]
-        keycodes[backspace] = unicode('backspace')
+        keycodes[backspace] = str('backspace')
     k = keymap.compile_keymap(keycodes)
     trace('keymap {k!r}', k=k)
     return EncodedQueue(k, encoding)
diff --git a/tests/infrastructure.py b/tests/infrastructure.py
index e90298b..2321389 100644
--- a/tests/infrastructure.py
+++ b/tests/infrastructure.py
@@ -17,7 +17,7 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-from __future__ import print_function
+
 from pyrepl.reader import Reader
 from pyrepl.console import Console, Event
 
diff --git a/tests/test_functional.py b/tests/test_functional.py
index f1f555b..f557ec0 100644
--- a/tests/test_functional.py
+++ b/tests/test_functional.py
@@ -24,7 +24,7 @@ def start_child():
     def start_child_func(env_update=None):
         assert not ret_childs, "child started already"
 
-        env = {k: v for k, v in os.environ.items() if k in (
+        env = {k: v for k, v in list(os.environ.items()) if k in (
             "TERM",
         )}
         if env_update:
diff --git a/tests/test_readline.py b/tests/test_readline.py
index a40aed0..9f55d1f 100644
--- a/tests/test_readline.py
+++ b/tests/test_readline.py
@@ -14,7 +14,7 @@ def readline_wrapper():
 
 if sys.version_info < (3, ):
     bytes_type = str
-    unicode_type = unicode  # noqa: F821
+    unicode_type = str  # noqa: F821
 else:
     bytes_type = bytes
     unicode_type = str
diff --git a/tests/test_unix_reader.py b/tests/test_unix_reader.py
index 9fbcb2c..287cb0f 100644
--- a/tests/test_unix_reader.py
+++ b/tests/test_unix_reader.py
@@ -1,11 +1,11 @@
-from __future__ import unicode_literals
+
 from pyrepl.unix_eventqueue import EncodedQueue, Event
 
 
 def test_simple():
     q = EncodedQueue({}, 'utf-8')
 
-    a = u'\u1234'
+    a = '\u1234'
     b = a.encode('utf-8')
     for c in b:
         q.push(c)

From ec29304db5550728d0e1b724779052c5b6d5c880 Mon Sep 17 00:00:00 2001
From: bretello <bretello@distruzione.org>
Date: Sun, 29 Oct 2023 15:07:02 +0100
Subject: [PATCH 6/9] format with black/isort

---
 pyrepl/_minimal_curses.py   |  12 +-
 pyrepl/cmdrepl.py           |  21 ++--
 pyrepl/commands.py          |  91 +++++++++++---
 pyrepl/completer.py         |  32 ++---
 pyrepl/completing_reader.py |  50 ++++----
 pyrepl/console.py           |  14 ++-
 pyrepl/curses.py            |   3 +-
 pyrepl/fancy_termios.py     |  34 ++++-
 pyrepl/historical_reader.py | 138 ++++++++++++--------
 pyrepl/input.py             |  20 +--
 pyrepl/keymap.py            | 157 +++++++++++++----------
 pyrepl/keymaps.py           | 214 ++++++++++++++++---------------
 pyrepl/module_lister.py     |  37 +++---
 pyrepl/pygame_console.py    | 127 +++++++++++--------
 pyrepl/pygame_keymap.py     | 204 +++++++++++++++++-------------
 pyrepl/python_reader.py     | 169 +++++++++++++++----------
 pyrepl/reader.py            | 242 ++++++++++++++++++------------------
 pyrepl/readline.py          | 140 ++++++++++-----------
 pyrepl/simple_interact.py   |  22 ++--
 pyrepl/trace.py             |   6 +-
 pyrepl/unix_console.py      | 171 +++++++++++++++----------
 pyrepl/unix_eventqueue.py   |  42 ++++---
 tests/infrastructure.py     |  15 ++-
 tests/test_basic.py         | 150 ++++++++++++++--------
 tests/test_bugs.py          |  21 ++--
 tests/test_curses.py        |   7 +-
 tests/test_functional.py    |  25 ++--
 tests/test_keymap.py        |  12 +-
 tests/test_readline.py      |  34 ++---
 tests/test_unix_reader.py   |  25 ++--
 tests/test_wishes.py        |  13 +-
 31 files changed, 1292 insertions(+), 956 deletions(-)

diff --git a/pyrepl/_minimal_curses.py b/pyrepl/_minimal_curses.py
index 79af03f..c31fa3e 100644
--- a/pyrepl/_minimal_curses.py
+++ b/pyrepl/_minimal_curses.py
@@ -17,7 +17,7 @@ class error(Exception):
 
 
 def _find_clib():
-    trylibs = ['ncursesw', 'ncurses', 'curses']
+    trylibs = ["ncursesw", "ncurses", "curses"]
 
     for lib in trylibs:
         path = ctypes.util.find_library(lib)
@@ -25,11 +25,11 @@ def _find_clib():
             return path
     raise ImportError("curses library not found")
 
+
 _clibpath = _find_clib()
 clib = ctypes.cdll.LoadLibrary(_clibpath)
 
-clib.setupterm.argtypes = [ctypes.c_char_p, ctypes.c_int,
-                           ctypes.POINTER(ctypes.c_int)]
+clib.setupterm.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(ctypes.c_int)]
 clib.setupterm.restype = ctypes.c_int
 
 clib.tigetstr.argtypes = [ctypes.c_char_p]
@@ -45,6 +45,7 @@ def _find_clib():
 
 try:
     from __pypy__ import builtinify
+
     builtinify  # silence broken pyflakes
 except ImportError:
     builtinify = lambda f: f
@@ -58,14 +59,13 @@ def setupterm(termstr, fd):
     err = ctypes.c_int(0)
     result = clib.setupterm(termstr, fd, ctypes.byref(err))
     if result == ERR:
-        raise error("setupterm(%r, %d) failed (err=%d)" % (
-            termstr, fd, err.value))
+        raise error("setupterm(%r, %d) failed (err=%d)" % (termstr, fd, err.value))
 
 
 @builtinify
 def tigetstr(cap):
     if not isinstance(cap, bytes):
-        cap = cap.encode('ascii')
+        cap = cap.encode("ascii")
     result = clib.tigetstr(cap)
     if ctypes.cast(result, ctypes.c_void_p).value == ERR:
         return None
diff --git a/pyrepl/cmdrepl.py b/pyrepl/cmdrepl.py
index 9bd1fe0..a790f07 100644
--- a/pyrepl/cmdrepl.py
+++ b/pyrepl/cmdrepl.py
@@ -34,17 +34,18 @@
 pyrepl."""
 
 
+import cmd
 
 from pyrepl import completer
 from pyrepl.completing_reader import CompletingReader as CR
-import cmd
 
 
 class CmdReader(CR):
     def collect_keymap(self):
         return super(CmdReader, self).collect_keymap() + (
             ("\\M-\\n", "invalid-key"),
-            ("\\n", "accept"))
+            ("\\n", "accept"),
+        )
 
     def __init__(self, completions):
         super(CmdReader, self).__init__()
@@ -53,13 +54,10 @@ def __init__(self, completions):
     def get_completions(self, stem):
         if len(stem) != self.pos:
             return []
-        return sorted(set(s
-                          for s in self.completions
-                          if s.startswith(stem)))
+        return sorted(set(s for s in self.completions if s.startswith(stem)))
 
 
 def replize(klass, history_across_invocations=1):
-
     """Return a subclass of the cmd.Cmd-derived klass that uses
     pyrepl instead of readline.
 
@@ -69,13 +67,13 @@ def replize(klass, history_across_invocations=1):
     controls whether instances of the returned class share
     histories."""
 
-    completions = [s[3:]
-                   for s in completer.get_class_members(klass)
-                   if s.startswith("do_")]
+    completions = [
+        s[3:] for s in completer.get_class_members(klass) if s.startswith("do_")
+    ]
 
     assert issubclass(klass, cmd.Cmd)
-#    if klass.cmdloop.im_class is not cmd.Cmd:
-#        print "this may not work"
+    #    if klass.cmdloop.im_class is not cmd.Cmd:
+    #        print "this may not work"
 
     class MultiHist(object):
         __history = []
@@ -118,4 +116,5 @@ def cmdloop(self, intro=None):
 
     class CmdRepl(hist, CmdLoopMixin, klass):
         __name__ = "replize(%s.%s)" % (klass.__module__, klass.__name__)
+
     return CmdRepl
diff --git a/pyrepl/commands.py b/pyrepl/commands.py
index 62322f8..b4ce55f 100644
--- a/pyrepl/commands.py
+++ b/pyrepl/commands.py
@@ -19,7 +19,8 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-import sys, os
+import os
+import sys
 
 # Catgories of actions:
 #  killing
@@ -30,6 +31,7 @@
 #  finishing
 # [completion]
 
+
 class Command(object):
     finish = 0
     kills_digit_arg = 1
@@ -42,6 +44,7 @@ def __init__(self, reader, event_name, event):
     def do(self):
         pass
 
+
 class KillCommand(Command):
     def kill_range(self, start, end):
         if start == end:
@@ -60,29 +63,38 @@ def kill_range(self, start, end):
         r.pos = start
         r.dirty = 1
 
+
 class YankCommand(Command):
     pass
 
+
 class MotionCommand(Command):
     pass
 
+
 class EditCommand(Command):
     pass
 
+
 class FinishCommand(Command):
     finish = 1
     pass
 
+
 def is_kill(command):
     return command and issubclass(command, KillCommand)
 
+
 def is_yank(command):
     return command and issubclass(command, YankCommand)
 
+
 # etc
 
+
 class digit_arg(Command):
     kills_digit_arg = 0
+
     def do(self):
         r = self.reader
         c = self.event[-1]
@@ -97,64 +109,74 @@ def do(self):
                 r.arg = d
             else:
                 if r.arg < 0:
-                    r.arg = 10*r.arg - d
+                    r.arg = 10 * r.arg - d
                 else:
-                    r.arg = 10*r.arg + d
+                    r.arg = 10 * r.arg + d
         r.dirty = 1
 
+
 class clear_screen(Command):
     def do(self):
         r = self.reader
         r.console.clear()
         r.dirty = 1
 
+
 class refresh(Command):
     def do(self):
         self.reader.dirty = 1
 
+
 class repaint(Command):
     def do(self):
         self.reader.dirty = 1
         self.reader.console.repaint_prep()
 
+
 class kill_line(KillCommand):
     def do(self):
         r = self.reader
         b = r.buffer
         eol = r.eol()
-        for c in b[r.pos:eol]:
+        for c in b[r.pos : eol]:
             if not c.isspace():
                 self.kill_range(r.pos, eol)
                 return
         else:
-            self.kill_range(r.pos, eol+1)
+            self.kill_range(r.pos, eol + 1)
+
 
 class unix_line_discard(KillCommand):
     def do(self):
         r = self.reader
         self.kill_range(r.bol(), r.pos)
 
+
 # XXX unix_word_rubout and backward_kill_word should actually
 # do different things...
 
+
 class unix_word_rubout(KillCommand):
     def do(self):
         r = self.reader
         for i in range(r.get_arg()):
             self.kill_range(r.bow(), r.pos)
 
+
 class kill_word(KillCommand):
     def do(self):
         r = self.reader
         for i in range(r.get_arg()):
             self.kill_range(r.pos, r.eow())
 
+
 class backward_kill_word(KillCommand):
     def do(self):
         r = self.reader
         for i in range(r.get_arg()):
             self.kill_range(r.bow(), r.pos)
 
+
 class yank(YankCommand):
     def do(self):
         r = self.reader
@@ -163,6 +185,7 @@ def do(self):
             return
         r.insert(r.kill_ring[-1])
 
+
 class yank_pop(YankCommand):
     def do(self):
         r = self.reader
@@ -176,19 +199,23 @@ def do(self):
         repl = len(r.kill_ring[-1])
         r.kill_ring.insert(0, r.kill_ring.pop())
         t = r.kill_ring[-1]
-        b[r.pos - repl:r.pos] = t
+        b[r.pos - repl : r.pos] = t
         r.pos = r.pos - repl + len(t)
         r.dirty = 1
 
+
 class interrupt(FinishCommand):
     def do(self):
         import signal
+
         self.reader.console.finish()
         os.kill(os.getpid(), signal.SIGINT)
 
+
 class suspend(Command):
     def do(self):
         import signal
+
         r = self.reader
         p = r.pos
         r.console.finish()
@@ -201,6 +228,7 @@ def do(self):
         r.dirty = 1
         r.console.screen = []
 
+
 class up(MotionCommand):
     def do(self):
         r = self.reader
@@ -213,7 +241,7 @@ def do(self):
                 r.pos = 0
                 r.error("start of buffer")
                 return
-            bol2 = r.bol(bol1-1)
+            bol2 = r.bol(bol1 - 1)
             line_pos = r.pos - bol1
             if line_pos > bol1 - bol2 - 1:
                 r.sticky_y = line_pos
@@ -221,6 +249,7 @@ def do(self):
             else:
                 r.pos = bol2 + line_pos
 
+
 class down(MotionCommand):
     def do(self):
         r = self.reader
@@ -236,22 +265,24 @@ def do(self):
                 r.pos = len(b)
                 r.error("end of buffer")
                 return
-            eol2 = r.eol(eol1+1)
+            eol2 = r.eol(eol1 + 1)
             if r.pos - bol1 > eol2 - eol1 - 1:
                 r.pos = eol2
             else:
                 r.pos = eol1 + (r.pos - bol1) + 1
 
+
 class left(MotionCommand):
     def do(self):
         r = self.reader
-        for i in range(r.get_arg()):        
+        for i in range(r.get_arg()):
             p = r.pos - 1
             if p >= 0:
                 r.pos = p
             else:
                 self.reader.error("start of buffer")
 
+
 class right(MotionCommand):
     def do(self):
         r = self.reader
@@ -263,45 +294,54 @@ def do(self):
             else:
                 self.reader.error("end of buffer")
 
+
 class beginning_of_line(MotionCommand):
     def do(self):
         self.reader.pos = self.reader.bol()
 
+
 class end_of_line(MotionCommand):
     def do(self):
         r = self.reader
         self.reader.pos = self.reader.eol()
 
+
 class home(MotionCommand):
     def do(self):
         self.reader.pos = 0
-        
+
+
 class end(MotionCommand):
     def do(self):
         self.reader.pos = len(self.reader.buffer)
-        
+
+
 class forward_word(MotionCommand):
     def do(self):
         r = self.reader
         for i in range(r.get_arg()):
             r.pos = r.eow()
-    
+
+
 class backward_word(MotionCommand):
     def do(self):
         r = self.reader
         for i in range(r.get_arg()):
             r.pos = r.bow()
 
+
 class self_insert(EditCommand):
     def do(self):
         r = self.reader
         r.insert(self.event * r.get_arg())
 
+
 class insert_nl(EditCommand):
     def do(self):
         r = self.reader
         r.insert("\n" * r.get_arg())
 
+
 class transpose_characters(EditCommand):
     def do(self):
         r = self.reader
@@ -319,6 +359,7 @@ def do(self):
             r.pos = t
             r.dirty = 1
 
+
 class backspace(EditCommand):
     def do(self):
         r = self.reader
@@ -331,12 +372,16 @@ def do(self):
             else:
                 self.reader.error("can't backspace at start")
 
+
 class delete(EditCommand):
     def do(self):
         r = self.reader
         b = r.buffer
-        if  ( r.pos == 0 and len(b) == 0 # this is something of a hack
-              and self.event[-1] == "\004"):
+        if (
+            r.pos == 0
+            and len(b) == 0  # this is something of a hack
+            and self.event[-1] == "\004"
+        ):
             r.update_screen()
             r.console.finish()
             raise EOFError
@@ -347,25 +392,30 @@ def do(self):
             else:
                 self.reader.error("end of buffer")
 
+
 class accept(FinishCommand):
     def do(self):
         pass
 
+
 class help(Command):
     def do(self):
         self.reader.msg = self.reader.help_text
         self.reader.dirty = 1
 
+
 class invalid_key(Command):
     def do(self):
         pending = self.reader.console.getpending()
-        s = ''.join(self.event) + pending.data
-        self.reader.error("`%r' not bound"%s)
+        s = "".join(self.event) + pending.data
+        self.reader.error("`%r' not bound" % s)
+
 
 class invalid_command(Command):
     def do(self):
         s = self.event_name
-        self.reader.error("command `%s' not known"%s)
+        self.reader.error("command `%s' not known" % s)
+
 
 class qIHelp(Command):
     def do(self):
@@ -377,15 +427,20 @@ def do(self):
         r.insert(disp * r.get_arg())
         r.pop_input_trans()
 
+
 from pyrepl import input
 
+
 class QITrans(object):
     def push(self, evt):
         self.evt = evt
+
     def get(self):
-        return ('qIHelp', self.evt.data)
+        return ("qIHelp", self.evt.data)
+
 
 class quoted_insert(Command):
     kills_digit_arg = 0
+
     def do(self):
         self.reader.push_input_trans(QITrans())
diff --git a/pyrepl/completer.py b/pyrepl/completer.py
index d44f4b9..c768481 100644
--- a/pyrepl/completer.py
+++ b/pyrepl/completer.py
@@ -17,22 +17,19 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-try:
-    import builtins as builtins
-    builtins  # silence broken pyflakes
-except ImportError:
-    import builtins
+import builtins
 
 
-class Completer(object):
+class Completer:
     def __init__(self, ns):
+        print(f"init with {ns=}")
         self.ns = ns
 
     def complete(self, text):
         if "." in text:
             return self.attr_matches(text)
-        else:
-            return self.global_matches(text)
+
+        return self.global_matches(text)
 
     def global_matches(self, text):
         """Compute matches when text is a simple name.
@@ -42,11 +39,15 @@ def global_matches(self, text):
 
         """
         import keyword
+
         matches = []
-        for list in [keyword.kwlist,
-                     list(builtins.__dict__.keys()),
-                     list(self.ns.keys())]:
-            for word in list:
+
+        for list_ in [
+            keyword.kwlist,
+            list(builtins.__dict__.keys()),
+            list(self.ns.keys()),
+        ]:
+            for word in list_:
                 if word.startswith(text) and word != "__builtins__":
                     matches.append(word)
         return matches
@@ -65,14 +66,15 @@ def attr_matches(self, text):
 
         """
         import re
+
         m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
         if not m:
             return []
         expr, attr = m.group(1, 3)
         object = eval(expr, self.ns)
         words = dir(object)
-        if hasattr(object, '__class__'):
-            words.append('__class__')
+        if hasattr(object, "__class__"):
+            words.append("__class__")
             words = words + get_class_members(object.__class__)
         matches = []
         n = len(attr)
@@ -84,7 +86,7 @@ def attr_matches(self, text):
 
 def get_class_members(klass):
     ret = dir(klass)
-    if hasattr(klass, '__bases__'):
+    if hasattr(klass, "__bases__"):
         for base in klass.__bases__:
             ret = ret + get_class_members(base)
     return ret
diff --git a/pyrepl/completing_reader.py b/pyrepl/completing_reader.py
index c7d0a7a..34ed675 100644
--- a/pyrepl/completing_reader.py
+++ b/pyrepl/completing_reader.py
@@ -19,6 +19,7 @@
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 import re
+
 from pyrepl import commands, reader
 from pyrepl.reader import Reader
 
@@ -40,8 +41,9 @@ def prefix(wordlist, j=0):
 
 STRIPCOLOR_REGEX = re.compile(r"\x1B\[([0-9]{1,3}(;[0-9]{1,2})?)?[m|K]")
 
+
 def stripcolor(s):
-    return STRIPCOLOR_REGEX.sub('', s)
+    return STRIPCOLOR_REGEX.sub("", s)
 
 
 def real_len(s):
@@ -54,7 +56,7 @@ def left_align(s, maxlen):
         # too bad, we remove the color
         return stripped[:maxlen]
     padding = maxlen - len(stripped)
-    return s + ' '*padding
+    return s + " " * padding
 
 
 def build_menu(cons, wordlist, start, use_brackets, sort_in_column):
@@ -66,7 +68,7 @@ def build_menu(cons, wordlist, start, use_brackets, sort_in_column):
         padding = 2
     maxlen = min(max(list(map(real_len, wordlist))), cons.width - padding)
     cols = int(cons.width / (maxlen + padding))
-    rows = int((len(wordlist) - 1)/cols + 1)
+    rows = int((len(wordlist) - 1) / cols + 1)
 
     if sort_in_column:
         # sort_in_column=False (default)     sort_in_column=True
@@ -76,8 +78,8 @@ def build_menu(cons, wordlist, start, use_brackets, sort_in_column):
         #
         # "fill" the table with empty words, so we always have the same amout
         # of rows for each column
-        missing = cols*rows - len(wordlist)
-        wordlist = wordlist + ['']*missing
+        missing = cols * rows - len(wordlist)
+        wordlist = wordlist + [""] * missing
         indexes = [(i % cols) * rows + i // cols for i in range(len(wordlist))]
         wordlist = [wordlist[i] for i in indexes]
     menu = []
@@ -89,7 +91,7 @@ def build_menu(cons, wordlist, start, use_brackets, sort_in_column):
             i += 1
             if i >= len(wordlist):
                 break
-        menu.append(''.join(row))
+        menu.append("".join(row))
         if i >= len(wordlist):
             i = 0
             break
@@ -98,6 +100,7 @@ def build_menu(cons, wordlist, start, use_brackets, sort_in_column):
             break
     return menu, i
 
+
 # this gets somewhat user interface-y, and as a result the logic gets
 # very convoluted.
 #
@@ -163,7 +166,7 @@ def do(self):
             if completions_unchangable and len(completions[0]) == len(stem):
                 r.msg = "[ sole completion ]"
                 r.dirty = 1
-            r.insert(completions[0][len(stem):])
+            r.insert(completions[0][len(stem) :])
         else:
             p = prefix(completions, len(stem))
             if p:
@@ -172,8 +175,12 @@ def do(self):
                 if not r.cmpltn_menu_vis:
                     r.cmpltn_menu_vis = 1
                 r.cmpltn_menu, r.cmpltn_menu_end = build_menu(
-                    r.console, completions, r.cmpltn_menu_end,
-                    r.use_brackets, r.sort_in_column)
+                    r.console,
+                    completions,
+                    r.cmpltn_menu_end,
+                    r.use_brackets,
+                    r.sort_in_column,
+                )
                 r.dirty = 1
             elif stem + p in completions:
                 r.msg = "[ complete but not unique ]"
@@ -192,12 +199,11 @@ def do(self):
             if len(stem) < 1:
                 r.cmpltn_reset()
             else:
-                completions = [w for w in r.cmpltn_menu_choices
-                               if w.startswith(stem)]
+                completions = [w for w in r.cmpltn_menu_choices if w.startswith(stem)]
                 if completions:
                     r.cmpltn_menu, r.cmpltn_menu_end = build_menu(
-                        r.console, completions, 0,
-                        r.use_brackets, r.sort_in_column)
+                        r.console, completions, 0, r.use_brackets, r.sort_in_column
+                    )
                 else:
                     r.cmpltn_reset()
 
@@ -209,14 +215,14 @@ class CompletingReader(Reader):
       * cmpltn_menu, cmpltn_menu_vis, cmpltn_menu_end, cmpltn_choices:
       *
     """
+
     # see the comment for the complete command
     assume_immutable_completions = True
     use_brackets = True  # display completions inside []
     sort_in_column = False
 
     def collect_keymap(self):
-        return super(CompletingReader, self).collect_keymap() + (
-            (r'\t', 'complete'),)
+        return super(CompletingReader, self).collect_keymap() + ((r"\t", "complete"),)
 
     def __init__(self, console):
         super(CompletingReader, self).__init__(console)
@@ -225,7 +231,7 @@ def __init__(self, console):
         self.cmpltn_menu_end = 0
         for c in (complete, self_insert):
             self.commands[c.__name__] = c
-            self.commands[c.__name__.replace('_', '-')] = c
+            self.commands[c.__name__.replace("_", "-")] = c
 
     def after_command(self, cmd):
         super(CompletingReader, self).after_command(cmd)
@@ -237,7 +243,7 @@ def calc_screen(self):
         if self.cmpltn_menu_vis:
             ly = self.lxy[1]
             screen[ly:ly] = self.cmpltn_menu
-            self.screeninfo[ly:ly] = [(0, [])]*len(self.cmpltn_menu)
+            self.screeninfo[ly:ly] = [(0, [])] * len(self.cmpltn_menu)
             self.cxy = self.cxy[0], self.cxy[1] + len(self.cmpltn_menu)
         return screen
 
@@ -258,7 +264,7 @@ def get_stem(self):
         p = self.pos - 1
         while p >= 0 and st.get(b[p], SW) == SW:
             p -= 1
-        return ''.join(b[p+1:self.pos])
+        return "".join(b[p + 1 : self.pos])
 
     def get_completions(self, stem):
         return []
@@ -267,9 +273,9 @@ def get_completions(self, stem):
 def test():
     class TestReader(CompletingReader):
         def get_completions(self, stem):
-            return [s for l in self.history
-                    for s in l.split()
-                    if s and s.startswith(stem)]
+            return [
+                s for l in self.history for s in l.split() if s and s.startswith(stem)
+            ]
 
     reader = TestReader()
     reader.ps1 = "c**> "
@@ -280,5 +286,5 @@ def get_completions(self, stem):
         pass
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
diff --git a/pyrepl/console.py b/pyrepl/console.py
index cfbcbe9..95bc78d 100644
--- a/pyrepl/console.py
+++ b/pyrepl/console.py
@@ -20,20 +20,22 @@
 
 class Event(object):
     """An Event.  `evt' is 'key' or somesuch."""
-    __slots__ = 'evt', 'data', 'raw'
 
-    def __init__(self, evt, data, raw=''):
+    __slots__ = "evt", "data", "raw"
+
+    def __init__(self, evt, data, raw=""):
         self.evt = evt
         self.data = data
         self.raw = raw
 
     def __repr__(self):
-        return 'Event(%r, %r)' % (self.evt, self.data)
+        return "Event(%r, %r)" % (self.evt, self.data)
 
     def __eq__(self, other):
-        return (self.evt == other.evt and
-                self.data == other.data and
-                self.raw == other.raw)
+        return (
+            self.evt == other.evt and self.data == other.data and self.raw == other.raw
+        )
+
 
 class Console(object):
     """Attributes:
diff --git a/pyrepl/curses.py b/pyrepl/curses.py
index 0331ce0..6ae8aaa 100644
--- a/pyrepl/curses.py
+++ b/pyrepl/curses.py
@@ -1,4 +1,3 @@
-
 #   Copyright 2000-2010 Michael Hudson-Doyle <micahel@gmail.com>
 #                       Armin Rigo
 #
@@ -20,4 +19,4 @@
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 
-from ._minimal_curses import setupterm, tigetstr, tparm, error
+from ._minimal_curses import error, setupterm, tigetstr, tparm
diff --git a/pyrepl/fancy_termios.py b/pyrepl/fancy_termios.py
index 462f7c6..5b85cb0 100644
--- a/pyrepl/fancy_termios.py
+++ b/pyrepl/fancy_termios.py
@@ -19,34 +19,56 @@
 
 import termios
 
+
 class TermState:
     def __init__(self, tuples):
-        self.iflag, self.oflag, self.cflag, self.lflag, \
-                    self.ispeed, self.ospeed, self.cc = tuples
+        (
+            self.iflag,
+            self.oflag,
+            self.cflag,
+            self.lflag,
+            self.ispeed,
+            self.ospeed,
+            self.cc,
+        ) = tuples
+
     def as_list(self):
-        return [self.iflag, self.oflag, self.cflag, self.lflag,
-                self.ispeed, self.ospeed, self.cc]
+        return [
+            self.iflag,
+            self.oflag,
+            self.cflag,
+            self.lflag,
+            self.ispeed,
+            self.ospeed,
+            self.cc,
+        ]
 
     def copy(self):
         return self.__class__(self.as_list())
 
+
 def tcgetattr(fd):
     return TermState(termios.tcgetattr(fd))
 
+
 def tcsetattr(fd, when, attrs):
     termios.tcsetattr(fd, when, attrs.as_list())
 
+
 class Term(TermState):
     TS__init__ = TermState.__init__
+
     def __init__(self, fd=0):
         self.TS__init__(termios.tcgetattr(fd))
         self.fd = fd
         self.stack = []
+
     def save(self):
-        self.stack.append( self.as_list() )
+        self.stack.append(self.as_list())
+
     def set(self, when=termios.TCSANOW):
         termios.tcsetattr(self.fd, when, self.as_list())
+
     def restore(self):
         self.TS__init__(self.stack.pop())
         self.set()
-        
diff --git a/pyrepl/historical_reader.py b/pyrepl/historical_reader.py
index d21ddb7..1ea436c 100644
--- a/pyrepl/historical_reader.py
+++ b/pyrepl/historical_reader.py
@@ -17,28 +17,38 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-from pyrepl import reader, commands
+from pyrepl import commands, reader
 from pyrepl.reader import Reader as R
 
 isearch_keymap = tuple(
-    [('\\%03o'%c, 'isearch-end') for c in range(256) if chr(c) != '\\'] + \
-    [(c, 'isearch-add-character')
-     for c in map(chr, list(range(32, 127))) if c != '\\'] + \
-    [('\\%03o'%c, 'isearch-add-character')
-     for c in range(256) if chr(c).isalpha() and chr(c) != '\\'] + \
-    [('\\\\', 'self-insert'),
-     (r'\C-r', 'isearch-backwards'),
-     (r'\C-s', 'isearch-forwards'),
-     (r'\C-c', 'isearch-cancel'),
-     (r'\C-g', 'isearch-cancel'),
-     (r'\<backspace>', 'isearch-backspace')])
-
-if 'c' in globals():
+    [("\\%03o" % c, "isearch-end") for c in range(256) if chr(c) != "\\"]
+    + [
+        (c, "isearch-add-character")
+        for c in map(chr, list(range(32, 127)))
+        if c != "\\"
+    ]
+    + [
+        ("\\%03o" % c, "isearch-add-character")
+        for c in range(256)
+        if chr(c).isalpha() and chr(c) != "\\"
+    ]
+    + [
+        ("\\\\", "self-insert"),
+        (r"\C-r", "isearch-backwards"),
+        (r"\C-s", "isearch-forwards"),
+        (r"\C-c", "isearch-cancel"),
+        (r"\C-g", "isearch-cancel"),
+        (r"\<backspace>", "isearch-backspace"),
+    ]
+)
+
+if "c" in globals():
     del c
 
-ISEARCH_DIRECTION_NONE = ''
-ISEARCH_DIRECTION_BACKWARDS = 'r'
-ISEARCH_DIRECTION_FORWARDS = 'f'
+ISEARCH_DIRECTION_NONE = ""
+ISEARCH_DIRECTION_BACKWARDS = "r"
+ISEARCH_DIRECTION_FORWARDS = "f"
+
 
 class next_history(commands.Command):
     def do(self):
@@ -48,6 +58,7 @@ def do(self):
             return
         r.select_item(r.historyi + 1)
 
+
 class previous_history(commands.Command):
     def do(self):
         r = self.reader
@@ -56,6 +67,7 @@ def do(self):
             return
         r.select_item(r.historyi - 1)
 
+
 class restore_history(commands.Command):
     def do(self):
         r = self.reader
@@ -65,18 +77,22 @@ def do(self):
                 r.pos = len(r.buffer)
                 r.dirty = 1
 
+
 class first_history(commands.Command):
     def do(self):
         self.reader.select_item(0)
 
+
 class last_history(commands.Command):
     def do(self):
         self.reader.select_item(len(self.reader.history))
 
+
 class operate_and_get_next(commands.FinishCommand):
     def do(self):
         self.reader.next_history = self.reader.historyi + 1
 
+
 class yank_arg(commands.Command):
     def do(self):
         r = self.reader
@@ -99,30 +115,32 @@ def do(self):
             o = len(r.yank_arg_yanked)
         else:
             o = 0
-        b[r.pos - o:r.pos] = list(w)
+        b[r.pos - o : r.pos] = list(w)
         r.yank_arg_yanked = w
         r.pos += len(w) - o
         r.dirty = 1
 
+
 class forward_history_isearch(commands.Command):
     def do(self):
         r = self.reader
         r.isearch_direction = ISEARCH_DIRECTION_FORWARDS
         r.isearch_start = r.historyi, r.pos
-        r.isearch_term = ''
+        r.isearch_term = ""
         r.dirty = 1
         r.push_input_trans(r.isearch_trans)
-        
+
 
 class reverse_history_isearch(commands.Command):
     def do(self):
         r = self.reader
         r.isearch_direction = ISEARCH_DIRECTION_BACKWARDS
         r.dirty = 1
-        r.isearch_term = ''
+        r.isearch_term = ""
         r.push_input_trans(r.isearch_trans)
         r.isearch_start = r.historyi, r.pos
 
+
 class isearch_cancel(commands.Command):
     def do(self):
         r = self.reader
@@ -132,6 +150,7 @@ def do(self):
         r.pos = r.isearch_start[1]
         r.dirty = 1
 
+
 class isearch_add_character(commands.Command):
     def do(self):
         r = self.reader
@@ -139,9 +158,10 @@ def do(self):
         r.isearch_term += self.event[-1]
         r.dirty = 1
         p = r.pos + len(r.isearch_term) - 1
-        if b[p:p+1] != [r.isearch_term[-1]]:
+        if b[p : p + 1] != [r.isearch_term[-1]]:
             r.isearch_next()
 
+
 class isearch_backspace(commands.Command):
     def do(self):
         r = self.reader
@@ -151,18 +171,21 @@ def do(self):
         else:
             r.error("nothing to rubout")
 
+
 class isearch_forwards(commands.Command):
     def do(self):
         r = self.reader
         r.isearch_direction = ISEARCH_DIRECTION_FORWARDS
         r.isearch_next()
 
+
 class isearch_backwards(commands.Command):
     def do(self):
         r = self.reader
         r.isearch_direction = ISEARCH_DIRECTION_BACKWARDS
         r.isearch_next()
 
+
 class isearch_end(commands.Command):
     def do(self):
         r = self.reader
@@ -171,6 +194,7 @@ def do(self):
         r.pop_input_trans()
         r.dirty = 1
 
+
 class HistoricalReader(R):
     """Adds history support (with incremental history searching) to the
     Reader class.
@@ -189,16 +213,16 @@ class HistoricalReader(R):
 
     def collect_keymap(self):
         return super(HistoricalReader, self).collect_keymap() + (
-            (r'\C-n', 'next-history'),
-            (r'\C-p', 'previous-history'),
-            (r'\C-o', 'operate-and-get-next'),
-            (r'\C-r', 'reverse-history-isearch'),
-            (r'\C-s', 'forward-history-isearch'),
-            (r'\M-r', 'restore-history'),
-            (r'\M-.', 'yank-arg'),
-            (r'\<page down>', 'last-history'),
-            (r'\<page up>', 'first-history'))
-
+            (r"\C-n", "next-history"),
+            (r"\C-p", "previous-history"),
+            (r"\C-o", "operate-and-get-next"),
+            (r"\C-r", "reverse-history-isearch"),
+            (r"\C-s", "forward-history-isearch"),
+            (r"\M-r", "restore-history"),
+            (r"\M-.", "yank-arg"),
+            (r"\<page down>", "last-history"),
+            (r"\<page up>", "first-history"),
+        )
 
     def __init__(self, console):
         super(HistoricalReader, self).__init__(console)
@@ -207,19 +231,32 @@ def __init__(self, console):
         self.transient_history = {}
         self.next_history = None
         self.isearch_direction = ISEARCH_DIRECTION_NONE
-        for c in [next_history, previous_history, restore_history,
-                  first_history, last_history, yank_arg,
-                  forward_history_isearch, reverse_history_isearch,
-                  isearch_end, isearch_add_character, isearch_cancel,
-                  isearch_add_character, isearch_backspace,
-                  isearch_forwards, isearch_backwards, operate_and_get_next]:
+        for c in [
+            next_history,
+            previous_history,
+            restore_history,
+            first_history,
+            last_history,
+            yank_arg,
+            forward_history_isearch,
+            reverse_history_isearch,
+            isearch_end,
+            isearch_add_character,
+            isearch_cancel,
+            isearch_add_character,
+            isearch_backspace,
+            isearch_forwards,
+            isearch_backwards,
+            operate_and_get_next,
+        ]:
             self.commands[c.__name__] = c
-            self.commands[c.__name__.replace('_', '-')] = c
+            self.commands[c.__name__.replace("_", "-")] = c
         from pyrepl import input
+
         self.isearch_trans = input.KeymapTranslator(
-            isearch_keymap, invalid_cls=isearch_end,
-            character_cls=isearch_add_character)
-        
+            isearch_keymap, invalid_cls=isearch_end, character_cls=isearch_add_character
+        )
+
     def select_item(self, i):
         self.transient_history[self.historyi] = self.get_unicode()
         buf = self.transient_history.get(i)
@@ -240,12 +277,11 @@ def prepare(self):
         super(HistoricalReader, self).prepare()
         try:
             self.transient_history = {}
-            if self.next_history is not None \
-               and self.next_history < len(self.history):
+            if self.next_history is not None and self.next_history < len(self.history):
                 self.historyi = self.next_history
                 self.buffer[:] = list(self.history[self.next_history])
                 self.pos = len(self.buffer)
-                self.transient_history[len(self.history)] = ''
+                self.transient_history[len(self.history)] = ""
             else:
                 self.historyi = len(self.history)
             self.next_history = None
@@ -255,8 +291,8 @@ def prepare(self):
 
     def get_prompt(self, lineno, cursor_on_line):
         if cursor_on_line and self.isearch_direction != ISEARCH_DIRECTION_NONE:
-            d = 'rf'[self.isearch_direction == ISEARCH_DIRECTION_FORWARDS]
-            return "(%s-search `%s') "%(d, self.isearch_term)
+            d = "rf"[self.isearch_direction == ISEARCH_DIRECTION_FORWARDS]
+            return "(%s-search `%s') " % (d, self.isearch_term)
         else:
             return super(HistoricalReader, self).get_prompt(lineno, cursor_on_line)
 
@@ -275,8 +311,7 @@ def isearch_next(self):
                 self.select_item(i)
                 self.pos = p
                 return
-            elif ((forwards and i == len(self.history) - 1)
-                  or (not forwards and i == 0)):
+            elif (forwards and i == len(self.history) - 1) or (not forwards and i == 0):
                 self.error("not found")
                 return
             else:
@@ -298,8 +333,10 @@ def finish(self):
         if ret:
             self.history.append(ret)
 
+
 def test():
     from pyrepl.unix_console import UnixConsole
+
     reader = HistoricalReader(UnixConsole())
     reader.ps1 = "h**> "
     reader.ps2 = "h/*> "
@@ -308,5 +345,6 @@ def test():
     while reader.readline():
         pass
 
-if __name__=='__main__':
+
+if __name__ == "__main__":
     test()
diff --git a/pyrepl/input.py b/pyrepl/input.py
index 36c0a1d..5180c5f 100644
--- a/pyrepl/input.py
+++ b/pyrepl/input.py
@@ -33,27 +33,29 @@
 # [meta-key] is identified with [esc key].  We demand that any console
 # class does quite a lot towards emulating a unix terminal.
 
+import pprint
 import unicodedata
 from collections import deque
-import pprint
+
 from .trace import trace
 
 
 class InputTranslator(object):
     def push(self, evt):
         pass
+
     def get(self):
         pass
+
     def empty(self):
         pass
 
 
 class KeymapTranslator(InputTranslator):
-
-    def __init__(self, keymap, verbose=0,
-                 invalid_cls=None, character_cls=None):
+    def __init__(self, keymap, verbose=0, invalid_cls=None, character_cls=None):
         self.verbose = verbose
         from pyrepl.keymap import compile_keymap, parse_keys
+
         self.keymap = keymap
         self.invalid_cls = invalid_cls
         self.character_cls = character_cls
@@ -62,7 +64,7 @@ def __init__(self, keymap, verbose=0,
             keyseq = tuple(parse_keys(keyspec))
             d[keyseq] = command
         if verbose:
-            trace('[input] keymap: {}', pprint.pformat(d))
+            trace("[input] keymap: {}", pprint.pformat(d))
         self.k = self.ck = compile_keymap(d, ())
         self.results = deque()
         self.stack = []
@@ -78,14 +80,12 @@ def push(self, evt):
         else:
             if d is None:
                 trace("[input] invalid")
-                if self.stack or len(key) > 1 or unicodedata.category(key) == 'C':
-                    self.results.append(
-                        (self.invalid_cls, self.stack + [key]))
+                if self.stack or len(key) > 1 or unicodedata.category(key) == "C":
+                    self.results.append((self.invalid_cls, self.stack + [key]))
                 else:
                     # small optimization:
                     self.k[key] = self.character_cls
-                    self.results.append(
-                        (self.character_cls, [key]))
+                    self.results.append((self.character_cls, [key]))
             else:
                 trace("[input] matched {}", d)
                 self.results.append((d, self.stack + [key]))
diff --git a/pyrepl/keymap.py b/pyrepl/keymap.py
index 830675e..e13c929 100644
--- a/pyrepl/keymap.py
+++ b/pyrepl/keymap.py
@@ -54,116 +54,141 @@
 """
 
 _escapes = {
-    '\\':'\\',
-    "'":"'",
-    '"':'"',
-    'a':'\a',
-    'b':r'\h',
-    'e':'\033',
-    'f':'\f',
-    'n':'\n',
-    'r':'\r',
-    't':'\t',
-    'v':'\v'
-    }
+    "\\": "\\",
+    "'": "'",
+    '"': '"',
+    "a": "\a",
+    "b": r"\h",
+    "e": "\033",
+    "f": "\f",
+    "n": "\n",
+    "r": "\r",
+    "t": "\t",
+    "v": "\v",
+}
 
 _keynames = {
-    'backspace': 'backspace',
-    'delete':    'delete',
-    'down':      'down',
-    'end':       'end',
-    'enter':     '\r',
-    'escape':    '\033',
-    'f1' : 'f1',   'f2' : 'f2',   'f3' : 'f3',   'f4' : 'f4',
-    'f5' : 'f5',   'f6' : 'f6',   'f7' : 'f7',   'f8' : 'f8',
-    'f9' : 'f9',   'f10': 'f10',  'f11': 'f11',  'f12': 'f12',
-    'f13': 'f13',  'f14': 'f14',  'f15': 'f15',  'f16': 'f16',
-    'f17': 'f17',  'f18': 'f18',  'f19': 'f19',  'f20': 'f20',
-    'home':      'home',
-    'insert':    'insert',
-    'left':      'left',
-    'page down': 'page down',
-    'page up':   'page up',
-    'return':    '\r',
-    'right':     'right',
-    'space':     ' ',
-    'tab':       '\t',
-    'up':        'up',
-    'ctrl left': 'ctrl left',
-    'ctrl right': 'ctrl right',
-    }
+    "backspace": "backspace",
+    "delete": "delete",
+    "down": "down",
+    "end": "end",
+    "enter": "\r",
+    "escape": "\033",
+    "f1": "f1",
+    "f2": "f2",
+    "f3": "f3",
+    "f4": "f4",
+    "f5": "f5",
+    "f6": "f6",
+    "f7": "f7",
+    "f8": "f8",
+    "f9": "f9",
+    "f10": "f10",
+    "f11": "f11",
+    "f12": "f12",
+    "f13": "f13",
+    "f14": "f14",
+    "f15": "f15",
+    "f16": "f16",
+    "f17": "f17",
+    "f18": "f18",
+    "f19": "f19",
+    "f20": "f20",
+    "home": "home",
+    "insert": "insert",
+    "left": "left",
+    "page down": "page down",
+    "page up": "page up",
+    "return": "\r",
+    "right": "right",
+    "space": " ",
+    "tab": "\t",
+    "up": "up",
+    "ctrl left": "ctrl left",
+    "ctrl right": "ctrl right",
+}
+
 
 class KeySpecError(Exception):
     pass
 
+
 def _parse_key1(key, s):
     ctrl = 0
     meta = 0
-    ret = ''
+    ret = ""
     while not ret and s < len(key):
-        if key[s] == '\\':
-            c = key[s+1].lower()
+        if key[s] == "\\":
+            c = key[s + 1].lower()
             if c in _escapes:
                 ret = _escapes[c]
                 s += 2
             elif c == "c":
-                if key[s + 2] != '-':
+                if key[s + 2] != "-":
                     raise KeySpecError(
-                              "\\C must be followed by `-' (char %d of %s)"%(
-                        s + 2, repr(key)))
+                        "\\C must be followed by `-' (char %d of %s)"
+                        % (s + 2, repr(key))
+                    )
                 if ctrl:
-                    raise KeySpecError("doubled \\C- (char %d of %s)"%(
-                        s + 1, repr(key)))
+                    raise KeySpecError(
+                        "doubled \\C- (char %d of %s)" % (s + 1, repr(key))
+                    )
                 ctrl = 1
                 s += 3
             elif c == "m":
-                if key[s + 2] != '-':
+                if key[s + 2] != "-":
                     raise KeySpecError(
-                              "\\M must be followed by `-' (char %d of %s)"%(
-                        s + 2, repr(key)))
+                        "\\M must be followed by `-' (char %d of %s)"
+                        % (s + 2, repr(key))
+                    )
                 if meta:
-                    raise KeySpecError("doubled \\M- (char %d of %s)"%(
-                        s + 1, repr(key)))
+                    raise KeySpecError(
+                        "doubled \\M- (char %d of %s)" % (s + 1, repr(key))
+                    )
                 meta = 1
                 s += 3
             elif c.isdigit():
-                n = key[s+1:s+4]
+                n = key[s + 1 : s + 4]
                 ret = chr(int(n, 8))
                 s += 4
-            elif c == 'x':
-                n = key[s+2:s+4]
+            elif c == "x":
+                n = key[s + 2 : s + 4]
                 ret = chr(int(n, 16))
                 s += 4
-            elif c == '<':
-                t = key.find('>', s)
+            elif c == "<":
+                t = key.find(">", s)
                 if t == -1:
                     raise KeySpecError(
-                              "unterminated \\< starting at char %d of %s"%(
-                        s + 1, repr(key)))
-                ret = key[s+2:t].lower()
+                        "unterminated \\< starting at char %d of %s"
+                        % (s + 1, repr(key))
+                    )
+                ret = key[s + 2 : t].lower()
                 if ret not in _keynames:
                     raise KeySpecError(
-                              "unrecognised keyname `%s' at char %d of %s"%(
-                        ret, s + 2, repr(key)))
+                        "unrecognised keyname `%s' at char %d of %s"
+                        % (ret, s + 2, repr(key))
+                    )
                 ret = _keynames[ret]
                 s = t + 1
             else:
                 raise KeySpecError(
-                          "unknown backslash escape %s at char %d of %s"%(
-                    repr(c), s + 2, repr(key)))
+                    "unknown backslash escape %s at char %d of %s"
+                    % (repr(c), s + 2, repr(key))
+                )
         else:
             ret = key[s]
             s += 1
     if ctrl:
         if len(ret) > 1:
             raise KeySpecError("\\C- must be followed by a character")
-        ret = chr(ord(ret) & 0x1f)   # curses.ascii.ctrl()
+        ret = chr(ord(ret) & 0x1F)  # curses.ascii.ctrl()
     if meta:
-        ret = ['\033', ret]
+        ret = ["\033", ret]
     else:
         ret = [ret]
     return ret, s
 
+
 def parse_keys(key):
     s = 0
     r = []
@@ -172,7 +197,8 @@ def parse_keys(key):
         r.extend(k)
     return r
 
-def compile_keymap(keymap, empty=b''):
+
+def compile_keymap(keymap, empty=b""):
     r = {}
     for key, value in list(keymap.items()):
         if isinstance(key, bytes):
@@ -184,7 +210,8 @@ def compile_keymap(keymap, empty=b''):
         if empty in value:
             if len(value) != 1:
                 raise KeySpecError(
-                      "key definitions for %s clash"%(list(value.values()),))
+                    "key definitions for %s clash" % (list(value.values()),)
+                )
             else:
                 r[key] = value[empty]
         else:
diff --git a/pyrepl/keymaps.py b/pyrepl/keymaps.py
index c25207c..e9294e0 100644
--- a/pyrepl/keymaps.py
+++ b/pyrepl/keymaps.py
@@ -18,123 +18,121 @@
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 reader_emacs_keymap = tuple(
-    [(r'\C-a', 'beginning-of-line'),
-     (r'\C-b', 'left'),
-     (r'\C-c', 'interrupt'),
-     (r'\C-d', 'delete'),
-     (r'\C-e', 'end-of-line'),
-     (r'\C-f', 'right'),
-     (r'\C-g', 'cancel'),
-     (r'\C-h', 'backspace'),
-     (r'\C-j', 'self-insert'),
-     (r'\<return>', 'accept'),
-     (r'\C-k', 'kill-line'),
-     (r'\C-l', 'clear-screen'),
-#     (r'\C-m', 'accept'),
-     (r'\C-q', 'quoted-insert'),
-     (r'\C-t', 'transpose-characters'),
-     (r'\C-u', 'unix-line-discard'),
-     (r'\C-v', 'quoted-insert'),
-     (r'\C-w', 'unix-word-rubout'),
-     (r'\C-x\C-u', 'upcase-region'),
-     (r'\C-y', 'yank'),
-     (r'\C-z', 'suspend'),
-     
-     (r'\M-b', 'backward-word'),
-     (r'\M-c', 'capitalize-word'),
-     (r'\M-d', 'kill-word'),
-     (r'\M-f', 'forward-word'),
-     (r'\M-l', 'downcase-word'),
-     (r'\M-t', 'transpose-words'),
-     (r'\M-u', 'upcase-word'),
-     (r'\M-y', 'yank-pop'),
-     (r'\M--', 'digit-arg'),
-     (r'\M-0', 'digit-arg'),
-     (r'\M-1', 'digit-arg'),
-     (r'\M-2', 'digit-arg'),
-     (r'\M-3', 'digit-arg'),
-     (r'\M-4', 'digit-arg'),
-     (r'\M-5', 'digit-arg'),
-     (r'\M-6', 'digit-arg'),
-     (r'\M-7', 'digit-arg'),
-     (r'\M-8', 'digit-arg'),
-     (r'\M-9', 'digit-arg'),
-     (r'\M-\n', 'self-insert'),
-     (r'\<backslash>', 'self-insert')] + \
-    [(c, 'self-insert')
-     for c in map(chr, list(range(32, 127))) if c != '\\'] + \
-    [(c, 'self-insert')
-     for c in map(chr, list(range(128, 256))) if c.isalpha()] + \
-    [(r'\<up>', 'up'),
-     (r'\<down>', 'down'),
-     (r'\<left>', 'left'),
-     (r'\<right>', 'right'),
-     (r'\<insert>', 'quoted-insert'),
-     (r'\<delete>', 'delete'),
-     (r'\<backspace>', 'backspace'),
-     (r'\M-\<backspace>', 'backward-kill-word'),
-     (r'\<end>', 'end'),
-     (r'\<home>', 'home'),
-     (r'\<f1>', 'help'),
-     (r'\EOF', 'end'),  # the entries in the terminfo database for xterms
-     (r'\EOH', 'home'), # seem to be wrong.  this is a less than ideal
-                        # workaround
-     ])
+    [
+        (r"\C-a", "beginning-of-line"),
+        (r"\C-b", "left"),
+        (r"\C-c", "interrupt"),
+        (r"\C-d", "delete"),
+        (r"\C-e", "end-of-line"),
+        (r"\C-f", "right"),
+        (r"\C-g", "cancel"),
+        (r"\C-h", "backspace"),
+        (r"\C-j", "self-insert"),
+        (r"\<return>", "accept"),
+        (r"\C-k", "kill-line"),
+        (r"\C-l", "clear-screen"),
+        #     (r'\C-m', 'accept'),
+        (r"\C-q", "quoted-insert"),
+        (r"\C-t", "transpose-characters"),
+        (r"\C-u", "unix-line-discard"),
+        (r"\C-v", "quoted-insert"),
+        (r"\C-w", "unix-word-rubout"),
+        (r"\C-x\C-u", "upcase-region"),
+        (r"\C-y", "yank"),
+        (r"\C-z", "suspend"),
+        (r"\M-b", "backward-word"),
+        (r"\M-c", "capitalize-word"),
+        (r"\M-d", "kill-word"),
+        (r"\M-f", "forward-word"),
+        (r"\M-l", "downcase-word"),
+        (r"\M-t", "transpose-words"),
+        (r"\M-u", "upcase-word"),
+        (r"\M-y", "yank-pop"),
+        (r"\M--", "digit-arg"),
+        (r"\M-0", "digit-arg"),
+        (r"\M-1", "digit-arg"),
+        (r"\M-2", "digit-arg"),
+        (r"\M-3", "digit-arg"),
+        (r"\M-4", "digit-arg"),
+        (r"\M-5", "digit-arg"),
+        (r"\M-6", "digit-arg"),
+        (r"\M-7", "digit-arg"),
+        (r"\M-8", "digit-arg"),
+        (r"\M-9", "digit-arg"),
+        (r"\M-\n", "self-insert"),
+        (r"\<backslash>", "self-insert"),
+    ]
+    + [(c, "self-insert") for c in map(chr, list(range(32, 127))) if c != "\\"]
+    + [(c, "self-insert") for c in map(chr, list(range(128, 256))) if c.isalpha()]
+    + [
+        (r"\<up>", "up"),
+        (r"\<down>", "down"),
+        (r"\<left>", "left"),
+        (r"\<right>", "right"),
+        (r"\<insert>", "quoted-insert"),
+        (r"\<delete>", "delete"),
+        (r"\<backspace>", "backspace"),
+        (r"\M-\<backspace>", "backward-kill-word"),
+        (r"\<end>", "end"),
+        (r"\<home>", "home"),
+        (r"\<f1>", "help"),
+        (r"\EOF", "end"),  # the entries in the terminfo database for xterms
+        (r"\EOH", "home"),  # seem to be wrong.  this is a less than ideal
+        # workaround
+    ]
+)
 
 hist_emacs_keymap = reader_emacs_keymap + (
-    (r'\C-n', 'next-history'),
-    (r'\C-p', 'previous-history'),
-    (r'\C-o', 'operate-and-get-next'),
-    (r'\C-r', 'reverse-history-isearch'),
-    (r'\C-s', 'forward-history-isearch'),
-    (r'\M-r', 'restore-history'),
-    (r'\M-.', 'yank-arg'),
-    (r'\<page down>', 'last-history'),
-    (r'\<page up>', 'first-history'))
+    (r"\C-n", "next-history"),
+    (r"\C-p", "previous-history"),
+    (r"\C-o", "operate-and-get-next"),
+    (r"\C-r", "reverse-history-isearch"),
+    (r"\C-s", "forward-history-isearch"),
+    (r"\M-r", "restore-history"),
+    (r"\M-.", "yank-arg"),
+    (r"\<page down>", "last-history"),
+    (r"\<page up>", "first-history"),
+)
 
-comp_emacs_keymap = hist_emacs_keymap + (
-    (r'\t', 'complete'),)
+comp_emacs_keymap = hist_emacs_keymap + ((r"\t", "complete"),)
 
 python_emacs_keymap = comp_emacs_keymap + (
-    (r'\n', 'maybe-accept'),
-    (r'\M-\n', 'self-insert'))
-    
+    (r"\n", "maybe-accept"),
+    (r"\M-\n", "self-insert"),
+)
+
 reader_vi_insert_keymap = tuple(
-    [(c, 'self-insert')
-     for c in map(chr, list(range(32, 127))) if c != '\\'] + \
-    [(c, 'self-insert')
-     for c in map(chr, list(range(128, 256))) if c.isalpha()] + \
-    [(r'\C-d', 'delete'),
-     (r'\<backspace>', 'backspace'),
-     ('')])
+    [(c, "self-insert") for c in map(chr, list(range(32, 127))) if c != "\\"]
+    + [(c, "self-insert") for c in map(chr, list(range(128, 256))) if c.isalpha()]
+    + [(r"\C-d", "delete"), (r"\<backspace>", "backspace"), ("")]
+)
 
 reader_vi_command_keymap = tuple(
     [
-    ('E', 'enter-emacs-mode'),
-    ('R', 'enter-replace-mode'),
-    ('dw', 'delete-word'),
-    ('dd', 'delete-line'),
-    
-    ('h', 'left'),
-    ('i', 'enter-insert-mode'),
-    ('j', 'down'),
-    ('k', 'up'),
-    ('l', 'right'),
-    ('r', 'replace-char'),
-    ('w', 'forward-word'),
-    ('x', 'delete'),
-    ('.', 'repeat-edit'), # argh!
-    (r'\<insert>', 'enter-insert-mode'),
-     ] + 
-    [(c, 'digit-arg') for c in '01234567689'] +
-    [])
-   
+        ("E", "enter-emacs-mode"),
+        ("R", "enter-replace-mode"),
+        ("dw", "delete-word"),
+        ("dd", "delete-line"),
+        ("h", "left"),
+        ("i", "enter-insert-mode"),
+        ("j", "down"),
+        ("k", "up"),
+        ("l", "right"),
+        ("r", "replace-char"),
+        ("w", "forward-word"),
+        ("x", "delete"),
+        (".", "repeat-edit"),  # argh!
+        (r"\<insert>", "enter-insert-mode"),
+    ]
+    + [(c, "digit-arg") for c in "01234567689"]
+    + []
+)
 
-reader_keymaps = {
-    'emacs' : reader_emacs_keymap,
-    'vi-insert' : reader_vi_insert_keymap,
-    'vi-command' : reader_vi_command_keymap
-    }
 
-del c # from the listcomps
+reader_keymaps = {
+    "emacs": reader_emacs_keymap,
+    "vi-insert": reader_vi_insert_keymap,
+    "vi-command": reader_vi_command_keymap,
+}
 
+del c  # from the listcomps
diff --git a/pyrepl/module_lister.py b/pyrepl/module_lister.py
index f3d7b0f..5191a20 100644
--- a/pyrepl/module_lister.py
+++ b/pyrepl/module_lister.py
@@ -17,45 +17,50 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-import os, sys
+import os
+import sys
 
 # for the completion support.
 # this is all quite nastily written.
 _packages = {}
 
-def _make_module_list_dir(dir, suffs, prefix=''):
+
+def _make_module_list_dir(dir, suffs, prefix=""):
     l = []
     for fname in os.listdir(dir):
         file = os.path.join(dir, fname)
         if os.path.isfile(file):
             for suff in suffs:
                 if fname.endswith(suff):
-                    l.append( prefix + fname[:-len(suff)] )
+                    l.append(prefix + fname[: -len(suff)])
                     break
-        elif os.path.isdir(file) \
-             and os.path.exists(os.path.join(file, "__init__.py")):
-            l.append( prefix + fname )
+        elif os.path.isdir(file) and os.path.exists(os.path.join(file, "__init__.py")):
+            l.append(prefix + fname)
             _packages[prefix + fname] = _make_module_list_dir(
-                file, suffs, prefix + fname + '.' )
+                file, suffs, prefix + fname + "."
+            )
     return sorted(set(l))
 
+
 def _make_module_list():
     import imp
-    suffs = [x[0] for x in imp.get_suffixes() if x[0] != '.pyc']
+
+    suffs = [x[0] for x in imp.get_suffixes() if x[0] != ".pyc"]
     suffs.sort(reverse=True)
-    _packages[''] = list(sys.builtin_module_names)
+    _packages[""] = list(sys.builtin_module_names)
     for dir in sys.path:
-        if dir == '':
-            dir = '.'
+        if dir == "":
+            dir = "."
         if os.path.isdir(dir):
-            _packages[''] += _make_module_list_dir(dir, suffs)
-    _packages[''].sort()
+            _packages[""] += _make_module_list_dir(dir, suffs)
+    _packages[""].sort()
+
 
 def find_modules(stem):
-    l = stem.split('.')
-    pack = '.'.join(l[:-1])
+    l = stem.split(".")
+    pack = ".".join(l[:-1])
     try:
         mods = _packages[pack]
     except KeyError:
-        raise ImportError("can't find \"%s\" package" % pack)
+        raise ImportError('can\'t find "%s" package' % pack)
     return [mod for mod in mods if mod.startswith(stem)]
diff --git a/pyrepl/pygame_console.py b/pyrepl/pygame_console.py
index 4eedf50..50febbb 100644
--- a/pyrepl/pygame_console.py
+++ b/pyrepl/pygame_console.py
@@ -25,11 +25,13 @@
 # during command execution and zap the executor process.  Making this
 # work on non-Unix is expected to be even more entertaining.
 
+import types
+
+import pygame
 from pygame.locals import *
-from pyrepl.console import Console, Event
+
 from pyrepl import pygame_keymap
-import pygame
-import types
+from pyrepl.console import Console, Event
 
 lmargin = 5
 rmargin = 5
@@ -39,46 +41,59 @@
 try:
     bool
 except NameError:
+
     def bool(x):
         return not not x
 
-modcolors = {K_LCTRL:1,
-             K_RCTRL:1,
-             K_LMETA:1,
-             K_RMETA:1,
-             K_LALT:1,
-             K_RALT:1,
-             K_LSHIFT:1,
-             K_RSHIFT:1}
+
+modcolors = {
+    K_LCTRL: 1,
+    K_RCTRL: 1,
+    K_LMETA: 1,
+    K_RMETA: 1,
+    K_LALT: 1,
+    K_RALT: 1,
+    K_LSHIFT: 1,
+    K_RSHIFT: 1,
+}
+
 
 class colors:
-    fg = 250,240,230
+    fg = 250, 240, 230
     bg = 5, 5, 5
     cursor = 230, 0, 230
     margin = 5, 5, 15
 
+
 class FakeStdout:
     def __init__(self, con):
         self.con = con
+
     def write(self, text):
         self.con.write(text)
+
     def flush(self):
         pass
 
+
 class FakeStdin:
     def __init__(self, con):
         self.con = con
+
     def read(self, n=None):
         # argh!
         raise NotImplementedError
+
     def readline(self, n=None):
         from .reader import Reader
+
         try:
             # this isn't quite right: it will clobber any prompt that's
             # been printed.  Not sure how to get around this...
             return Reader(self.con).readline()
         except EOFError:
-            return ''
+            return ""
+
 
 class PyGameConsole(Console):
     """Attributes:
@@ -89,13 +104,12 @@ class PyGameConsole(Console):
     height,
     width,
     """
-    
+
     def __init__(self):
         self.pygame_screen = pygame.display.set_mode((800, 600))
         pygame.font.init()
         pygame.key.set_repeat(500, 30)
-        self.font = pygame.font.Font(
-            "/usr/X11R6/lib/X11/fonts/TTF/luximr.ttf", 15)
+        self.font = pygame.font.Font("/usr/X11R6/lib/X11/fonts/TTF/luximr.ttf", 15)
         self.fw, self.fh = self.fontsize = self.font.size("X")
         self.cursor = pygame.Surface(self.fontsize)
         self.cursor.fill(colors.cursor)
@@ -105,7 +119,7 @@ def __init__(self):
         pygame.display.update()
         pygame.event.set_allowed(None)
         pygame.event.set_allowed(KEYDOWN)
-        
+
     def install_keymap(self, keymap):
         """Install a given keymap.
 
@@ -119,8 +133,10 @@ def char_rect(self, x, y):
         return self.char_pos(x, y), self.fontsize
 
     def char_pos(self, x, y):
-        return (lmargin + x*self.fw,
-                tmargin + y*self.fh + self.cur_top + self.scroll)
+        return (
+            lmargin + x * self.fw,
+            tmargin + y * self.fh + self.cur_top + self.scroll,
+        )
 
     def paint_margin(self):
         s = self.pygame_screen
@@ -133,9 +149,9 @@ def paint_margin(self):
     def refresh(self, screen, xxx_todo_changeme):
         (cx, cy) = xxx_todo_changeme
         self.screen = screen
-        self.pygame_screen.fill(colors.bg,
-                                [0, tmargin + self.cur_top + self.scroll,
-                                 800, 600])
+        self.pygame_screen.fill(
+            colors.bg, [0, tmargin + self.cur_top + self.scroll, 800, 600]
+        )
         self.paint_margin()
 
         line_top = self.cur_top
@@ -143,7 +159,7 @@ def refresh(self, screen, xxx_todo_changeme):
         self.cxy = (cx, cy)
         cp = self.char_pos(cx, cy)
         if cp[1] < tmargin:
-            self.scroll = - (cy*self.fh + self.cur_top)
+            self.scroll = -(cy * self.fh + self.cur_top)
             self.repaint()
         elif cp[1] + self.fh > 600 - bmargin:
             self.scroll += (600 - bmargin) - (cp[1] + self.fh)
@@ -154,13 +170,14 @@ def refresh(self, screen, xxx_todo_changeme):
             if 0 <= line_top + self.scroll <= (600 - bmargin - tmargin - self.fh):
                 if line:
                     ren = self.font.render(line, 1, colors.fg)
-                    self.pygame_screen.blit(ren, (lmargin,
-                                                  tmargin + line_top + self.scroll))
+                    self.pygame_screen.blit(
+                        ren, (lmargin, tmargin + line_top + self.scroll)
+                    )
             line_top += self.fh
         pygame.display.update()
 
     def prepare(self):
-        self.cmd_buf = ''
+        self.cmd_buf = ""
         self.k = self.keymap
         self.height, self.width = self.getheightwidth()
         self.curs_vis = 1
@@ -179,7 +196,7 @@ def blit_a_char(self, linen, charn):
     def move_cursor(self, x, y):
         cp = self.char_pos(x, y)
         if cp[1] < tmargin or cp[1] + self.fh > 600 - bmargin:
-            self.event_queue.append(Event('refresh', '', ''))
+            self.event_queue.append(Event("refresh", "", ""))
         else:
             if self.curs_vis:
                 cx, cy = self.cxy
@@ -203,13 +220,15 @@ def set_cursor_vis(self, vis):
     def getheightwidth(self):
         """Return (height, width) where height and width are the height
         and width of the terminal window in characters."""
-        return ((600 - tmargin - bmargin)/self.fh,
-                (800 - lmargin - rmargin)/self.fw)
+        return (
+            (600 - tmargin - bmargin) / self.fh,
+            (800 - lmargin - rmargin) / self.fw,
+        )
 
     def tr_event(self, pyg_event):
         shift = bool(pyg_event.mod & KMOD_SHIFT)
         ctrl = bool(pyg_event.mod & KMOD_CTRL)
-        meta = bool(pyg_event.mod & (KMOD_ALT|KMOD_META))
+        meta = bool(pyg_event.mod & (KMOD_ALT | KMOD_META))
 
         try:
             return self.k[(pyg_event.str, meta, ctrl)], pyg_event.str
@@ -237,13 +256,13 @@ def get_event(self, block=1):
                 continue
 
             k, c = self.tr_event(pyg_event)
-            self.cmd_buf += c.encode('ascii', 'replace')
+            self.cmd_buf += c.encode("ascii", "replace")
             self.k = k
 
             if not isinstance(k, dict):
                 e = Event(k, self.cmd_buf, [])
                 self.k = self.keymap
-                self.cmd_buf = ''
+                self.cmd_buf = ""
                 return e
 
     def beep(self):
@@ -254,7 +273,7 @@ def beep(self):
     def clear(self):
         """Wipe the screen"""
         self.pygame_screen.fill(colors.bg)
-        #self.screen = []
+        # self.screen = []
         self.pos = [0, 0]
         self.grobs = []
         self.cur_top = 0
@@ -270,9 +289,10 @@ def finish(self):
         for line in self.screen:
             self.write_line(line, 1)
         if self.curs_vis:
-            self.pygame_screen.blit(self.cursor,
-                                    (lmargin + self.pos[1],
-                                     tmargin + self.pos[0] + self.scroll))
+            self.pygame_screen.blit(
+                self.cursor,
+                (lmargin + self.pos[1], tmargin + self.pos[0] + self.scroll),
+            )
         pygame.display.update()
 
     def flushoutput(self):
@@ -285,7 +305,7 @@ def forgetinput(self):
         """Forget all pending, but not yet processed input."""
         while pygame.event.poll().type != NOEVENT:
             pass
-    
+
     def getpending(self):
         """Return the characters that have been typed but not yet
         processed."""
@@ -308,21 +328,20 @@ def repaint(self):
         self.paint_margin()
         for (y, x), surf, text in self.grobs:
             if surf and 0 < y + self.scroll:
-                self.pygame_screen.blit(surf, (lmargin + x,
-                                               tmargin + y + self.scroll))
+                self.pygame_screen.blit(surf, (lmargin + x, tmargin + y + self.scroll))
         pygame.display.update()
 
     def write_line(self, line, ret):
-        charsleft = (self.width*self.fw - self.pos[1])/self.fw
+        charsleft = (self.width * self.fw - self.pos[1]) / self.fw
         while len(line) > charsleft:
             self.write_line(line[:charsleft], 1)
             line = line[charsleft:]
         if line:
             ren = self.font.render(line, 1, colors.fg, colors.bg)
             self.grobs.append((self.pos[:], ren, line))
-            self.pygame_screen.blit(ren,
-                                    (lmargin + self.pos[1],
-                                     tmargin + self.pos[0] + self.scroll))
+            self.pygame_screen.blit(
+                ren, (lmargin + self.pos[1], tmargin + self.pos[0] + self.scroll)
+            )
         else:
             self.grobs.append((self.pos[:], None, line))
         if ret:
@@ -332,22 +351,28 @@ def write_line(self, line, ret):
                 self.repaint()
             self.pos[1] = 0
         else:
-            self.pos[1] += self.fw*len(line)
+            self.pos[1] += self.fw * len(line)
 
     def write(self, text):
         lines = text.split("\n")
         if self.curs_vis:
-            self.pygame_screen.fill(colors.bg,
-                                    (lmargin + self.pos[1],
-                                     tmargin + self.pos[0] + self.scroll,
-                                     self.fw, self.fh))
+            self.pygame_screen.fill(
+                colors.bg,
+                (
+                    lmargin + self.pos[1],
+                    tmargin + self.pos[0] + self.scroll,
+                    self.fw,
+                    self.fh,
+                ),
+            )
         for line in lines[:-1]:
             self.write_line(line, 1)
         self.write_line(lines[-1], 0)
         if self.curs_vis:
-            self.pygame_screen.blit(self.cursor,
-                                    (lmargin + self.pos[1],
-                                     tmargin + self.pos[0] + self.scroll))
+            self.pygame_screen.blit(
+                self.cursor,
+                (lmargin + self.pos[1], tmargin + self.pos[0] + self.scroll),
+            )
         pygame.display.update()
 
     def flush(self):
diff --git a/pyrepl/pygame_keymap.py b/pyrepl/pygame_keymap.py
index b4018e4..f74d1bd 100644
--- a/pyrepl/pygame_keymap.py
+++ b/pyrepl/pygame_keymap.py
@@ -39,106 +39,134 @@
 from pygame.locals import *
 
 _escapes = {
-    '\\': K_BACKSLASH,
-    "'" : K_QUOTE,
-    '"' : K_QUOTEDBL,
-#    'a' : '\a',
-    'b' : K_BACKSLASH,
-    'e' : K_ESCAPE,
-#    'f' : '\f',
-    'n' : K_RETURN,
-    'r' : K_RETURN,
-    't' : K_TAB,
-#    'v' : '\v'
-    }
+    "\\": K_BACKSLASH,
+    "'": K_QUOTE,
+    '"': K_QUOTEDBL,
+    #    'a' : '\a',
+    "b": K_BACKSLASH,
+    "e": K_ESCAPE,
+    #    'f' : '\f',
+    "n": K_RETURN,
+    "r": K_RETURN,
+    "t": K_TAB,
+    #    'v' : '\v'
+}
 
 _keynames = {
-    'backspace' : K_BACKSPACE,
-    'delete'    : K_DELETE,
-    'down'      : K_DOWN,
-    'end'       : K_END,
-    'enter'     : K_KP_ENTER,
-    'escape'    : K_ESCAPE,
-    'f1' : K_F1, 'f2' : K_F2, 'f3' : K_F3, 'f4' : K_F4,
-    'f5' : K_F5, 'f6' : K_F6, 'f7' : K_F7, 'f8' : K_F8,
-    'f9' : K_F9, 'f10': K_F10,'f11': K_F11,'f12': K_F12,
-    'f13': K_F13,'f14': K_F14,'f15': K_F15,
-    'home'   : K_HOME,
-    'insert' : K_INSERT,
-    'left'   : K_LEFT,
-    'pgdown' : K_PAGEDOWN, 'page down' : K_PAGEDOWN,
-    'pgup'   : K_PAGEUP,   'page up'   : K_PAGEUP,
-    'return' : K_RETURN,
-    'right'  : K_RIGHT,
-    'space'  : K_SPACE,
-    'tab'    : K_TAB,
-    'up'     : K_UP,
-    }
+    "backspace": K_BACKSPACE,
+    "delete": K_DELETE,
+    "down": K_DOWN,
+    "end": K_END,
+    "enter": K_KP_ENTER,
+    "escape": K_ESCAPE,
+    "f1": K_F1,
+    "f2": K_F2,
+    "f3": K_F3,
+    "f4": K_F4,
+    "f5": K_F5,
+    "f6": K_F6,
+    "f7": K_F7,
+    "f8": K_F8,
+    "f9": K_F9,
+    "f10": K_F10,
+    "f11": K_F11,
+    "f12": K_F12,
+    "f13": K_F13,
+    "f14": K_F14,
+    "f15": K_F15,
+    "home": K_HOME,
+    "insert": K_INSERT,
+    "left": K_LEFT,
+    "pgdown": K_PAGEDOWN,
+    "page down": K_PAGEDOWN,
+    "pgup": K_PAGEUP,
+    "page up": K_PAGEUP,
+    "return": K_RETURN,
+    "right": K_RIGHT,
+    "space": K_SPACE,
+    "tab": K_TAB,
+    "up": K_UP,
+}
+
 
 class KeySpecError(Exception):
     pass
 
+
 def _parse_key1(key, s):
     ctrl = 0
     meta = 0
-    ret = ''
+    ret = ""
     while not ret and s < len(key):
-        if key[s] == '\\':
-            c = key[s+1].lower()
+        if key[s] == "\\":
+            c = key[s + 1].lower()
             if c in _escapes:
                 ret = _escapes[c]
                 s += 2
             elif c == "c":
-                if key[s + 2] != '-':
-                    raise KeySpecError("\\C must be followed by `-' (char %d of %s)"%(
-                        s + 2, repr(key)))
+                if key[s + 2] != "-":
+                    raise KeySpecError(
+                        "\\C must be followed by `-' (char %d of %s)"
+                        % (s + 2, repr(key))
+                    )
                 if ctrl:
-                    raise KeySpecError("doubled \\C- (char %d of %s)"%(
-                        s + 1, repr(key)))
+                    raise KeySpecError(
+                        "doubled \\C- (char %d of %s)" % (s + 1, repr(key))
+                    )
                 ctrl = 1
                 s += 3
             elif c == "m":
-                if key[s + 2] != '-':
-                    raise KeySpecError("\\M must be followed by `-' (char %d of %s)"%(
-                        s + 2, repr(key)))
+                if key[s + 2] != "-":
+                    raise KeySpecError(
+                        "\\M must be followed by `-' (char %d of %s)"
+                        % (s + 2, repr(key))
+                    )
                 if meta:
-                    raise KeySpecError("doubled \\M- (char %d of %s)"%(
-                        s + 1, repr(key)))
+                    raise KeySpecError(
+                        "doubled \\M- (char %d of %s)" % (s + 1, repr(key))
+                    )
                 meta = 1
                 s += 3
             elif c.isdigit():
-                n = key[s+1:s+4]
+                n = key[s + 1 : s + 4]
                 ret = chr(int(n, 8))
                 s += 4
-            elif c == 'x':
-                n = key[s+2:s+4]
+            elif c == "x":
+                n = key[s + 2 : s + 4]
                 ret = chr(int(n, 16))
                 s += 4
-            elif c == '<':
-                t = key.find('>', s)
+            elif c == "<":
+                t = key.find(">", s)
                 if t == -1:
-                    raise KeySpecError("unterminated \\< starting at char %d of %s"%(
-                        s + 1, repr(key)))
+                    raise KeySpecError(
+                        "unterminated \\< starting at char %d of %s"
+                        % (s + 1, repr(key))
+                    )
                 try:
-                    ret = _keynames[key[s+2:t].lower()]
+                    ret = _keynames[key[s + 2 : t].lower()]
                     s = t + 1
                 except KeyError:
-                    raise KeySpecError("unrecognised keyname `%s' at char %d of %s"%(
-                        key[s+2:t], s + 2, repr(key)))
+                    raise KeySpecError(
+                        "unrecognised keyname `%s' at char %d of %s"
+                        % (key[s + 2 : t], s + 2, repr(key))
+                    )
                 if ret is None:
                     return None, s
             else:
-                raise KeySpecError("unknown backslash escape %s at char %d of %s"%(
-                    repr(c), s + 2, repr(key)))
+                raise KeySpecError(
+                    "unknown backslash escape %s at char %d of %s"
+                    % (repr(c), s + 2, repr(key))
+                )
         else:
             if ctrl:
-                ret = chr(ord(key[s]) & 0x1f)   # curses.ascii.ctrl()
+                ret = chr(ord(key[s]) & 0x1F)  # curses.ascii.ctrl()
                 ret = str(ret)
             else:
                 ret = str(key[s])
             s += 1
     return (ret, meta, ctrl), s
 
+
 def parse_keys(key):
     s = 0
     r = []
@@ -149,6 +177,7 @@ def parse_keys(key):
         r.append(k)
     return tuple(r)
 
+
 def _compile_keymap(keymap):
     r = {}
     for key, value in list(keymap.items()):
@@ -156,13 +185,16 @@ def _compile_keymap(keymap):
     for key, value in list(r.items()):
         if () in value:
             if len(value) != 1:
-                raise KeySpecError("key definitions for %s clash"%(list(value.values()),))
+                raise KeySpecError(
+                    "key definitions for %s clash" % (list(value.values()),)
+                )
             else:
                 r[key] = value[()]
         else:
             r[key] = _compile_keymap(value)
     return r
 
+
 def compile_keymap(keymap):
     r = {}
     for key, value in keymap:
@@ -173,12 +205,12 @@ def compile_keymap(keymap):
             r[k] = value
     return _compile_keymap(r)
 
+
 def keyname(key):
-    longest_match = ''
-    longest_match_name = ''
+    longest_match = ""
+    longest_match_name = ""
     for name, keyseq in list(keyset.items()):
-        if keyseq and key.startswith(keyseq) and \
-               len(keyseq) > len(longest_match):
+        if keyseq and key.startswith(keyseq) and len(keyseq) > len(longest_match):
             longest_match = keyseq
             longest_match_name = name
     if len(longest_match) > 0:
@@ -186,59 +218,63 @@ def keyname(key):
     else:
         return None, 0
 
-_unescapes = {'\r':'\\r', '\n':'\\n', '\177':'^?'}
 
-#for k,v in _escapes.items():
+_unescapes = {"\r": "\\r", "\n": "\\n", "\177": "^?"}
+
+# for k,v in _escapes.items():
 #    _unescapes[v] = k
 
+
 def unparse_key(keyseq):
     if not keyseq:
-        return ''
+        return ""
     name, s = keyname(keyseq)
     if name:
-        if name != 'escape' or s == len(keyseq):
-            return '\\<' + name + '>' + unparse_key(keyseq[s:])
+        if name != "escape" or s == len(keyseq):
+            return "\\<" + name + ">" + unparse_key(keyseq[s:])
         else:
-            return '\\M-' + unparse_key(keyseq[1:])
+            return "\\M-" + unparse_key(keyseq[1:])
     else:
         c = keyseq[0]
         r = keyseq[1:]
-        if c == '\\':
-            p = '\\\\'
+        if c == "\\":
+            p = "\\\\"
         elif c in _unescapes:
             p = _unescapes[c]
-        elif ord(c) < ord(' '):
-            p = '\\C-%s'%(chr(ord(c)+96),)
-        elif ord(' ') <= ord(c) <= ord('~'):
+        elif ord(c) < ord(" "):
+            p = "\\C-%s" % (chr(ord(c) + 96),)
+        elif ord(" ") <= ord(c) <= ord("~"):
             p = c
         else:
-            p = '\\%03o'%(ord(c),)
+            p = "\\%03o" % (ord(c),)
         return p + unparse_key(r)
 
+
 def _unparse_keyf(keyseq):
     if not keyseq:
         return []
     name, s = keyname(keyseq)
     if name:
-        if name != 'escape' or s == len(keyseq):
+        if name != "escape" or s == len(keyseq):
             return [name] + _unparse_keyf(keyseq[s:])
         else:
             rest = _unparse_keyf(keyseq[1:])
-            return ['M-'+rest[0]] + rest[1:]
+            return ["M-" + rest[0]] + rest[1:]
     else:
         c = keyseq[0]
         r = keyseq[1:]
-        if c == '\\':
-            p = '\\'
+        if c == "\\":
+            p = "\\"
         elif c in _unescapes:
             p = _unescapes[c]
-        elif ord(c) < ord(' '):
-            p = 'C-%s'%(chr(ord(c)+96),)
-        elif ord(' ') <= ord(c) <= ord('~'):
+        elif ord(c) < ord(" "):
+            p = "C-%s" % (chr(ord(c) + 96),)
+        elif ord(" ") <= ord(c) <= ord("~"):
             p = c
         else:
-            p = '\\%03o'%(ord(c),)
+            p = "\\%03o" % (ord(c),)
         return [p] + _unparse_keyf(r)
 
+
 def unparse_keyf(keyseq):
     return " ".join(_unparse_keyf(keyseq))
diff --git a/pyrepl/python_reader.py b/pyrepl/python_reader.py
index f3a688e..73285db 100644
--- a/pyrepl/python_reader.py
+++ b/pyrepl/python_reader.py
@@ -22,13 +22,19 @@
 # one impressive collections of imports:
 
 
+import atexit
+import code
+import imp
+import os
+import re
+import sys
+import traceback
+import warnings
+
+from pyrepl import (commands, completer, completing_reader, module_lister,
+                    reader)
 from pyrepl.completing_reader import CompletingReader
 from pyrepl.historical_reader import HistoricalReader
-from pyrepl import completing_reader, reader
-from pyrepl import commands, completer
-from pyrepl import module_lister
-import imp, sys, os, re, code, traceback
-import atexit, warnings
 
 try:
     str
@@ -44,23 +50,27 @@
 
 CommandCompiler = code.CommandCompiler
 
+
 def eat_it(*args):
     """this function eats warnings, if you were wondering"""
     pass
 
-if sys.version_info >= (3,0):
+
+if sys.version_info >= (3, 0):
+
     def _reraise(cls, val, tb):
         __tracebackhide__ = True
-        assert hasattr(val, '__traceback__')
+        assert hasattr(val, "__traceback__")
         raise val
+
 else:
-    exec ("""
+    exec(
+        """
 def _reraise(cls, val, tb):
     __tracebackhide__ = True
     raise cls, val, tb
-""")
-
-
+"""
+    )
 
 
 class maybe_accept(commands.Command):
@@ -78,28 +88,32 @@ def do(self):
             else:
                 self.finish = 1
 
+
 from_line_prog = re.compile(
-    "^from\s+(?P<mod>[A-Za-z_.0-9]*)\s+import\s+(?P<name>[A-Za-z_.0-9]*)")
-import_line_prog = re.compile(
-    "^(?:import|from)\s+(?P<mod>[A-Za-z_.0-9]*)\s*$")
+    "^from\s+(?P<mod>[A-Za-z_.0-9]*)\s+import\s+(?P<name>[A-Za-z_.0-9]*)"
+)
+import_line_prog = re.compile("^(?:import|from)\s+(?P<mod>[A-Za-z_.0-9]*)\s*$")
+
 
 def saver(reader=reader):
     try:
         with open(os.path.expanduser("~/.pythoni.hist"), "wb") as fp:
-            fp.write(b'\n'.join(item.encode('unicode_escape')
-                                for item in reader.history))
+            fp.write(
+                b"\n".join(item.encode("unicode_escape") for item in reader.history)
+            )
     except IOError as e:
         print(e)
         pass
 
+
 class PythonicReader(CompletingReader, HistoricalReader):
     def collect_keymap(self):
         return super(PythonicReader, self).collect_keymap() + (
-            (r'\n', 'maybe-accept'),
-            (r'\M-\n', 'insert-nl'))
-    
-    def __init__(self, console, locals,
-                 compiler=None):
+            (r"\n", "maybe-accept"),
+            (r"\M-\n", "insert-nl"),
+        )
+
+    def __init__(self, console, locals, compiler=None):
         super(PythonicReader, self).__init__(console)
         self.completer = completer.Completer(locals)
         st = self.syntax_table
@@ -111,13 +125,13 @@ def __init__(self, console, locals,
         else:
             self.compiler = compiler
         try:
-            file = open(os.path.expanduser("~/.pythoni.hist"), 'rb')
+            file = open(os.path.expanduser("~/.pythoni.hist"), "rb")
         except IOError:
             self.history = []
         else:
             try:
                 lines = file.readlines()
-                self.history = [ x.rstrip(b'\n').decode('unicode_escape') for x in lines]
+                self.history = [x.rstrip(b"\n").decode("unicode_escape") for x in lines]
             except:
                 self.history = []
             self.historyi = len(self.history)
@@ -125,8 +139,8 @@ def __init__(self, console, locals,
         atexit.register(lambda: saver(self))
         for c in [maybe_accept]:
             self.commands[c.__name__] = c
-            self.commands[c.__name__.replace('_', '-')] = c        
-    
+            self.commands[c.__name__.replace("_", "-")] = c
+
     def get_completions(self, stem):
         b = self.get_unicode()
         m = import_line_prog.match(b)
@@ -147,21 +161,22 @@ def get_completions(self, stem):
                 l = module_lister._packages[mod]
             except KeyError:
                 try:
-                    mod = __import__(mod, self.locals, self.locals, [''])
+                    mod = __import__(mod, self.locals, self.locals, [""])
                     return [x for x in dir(mod) if x.startswith(name)]
                 except ImportError:
                     pass
             else:
-                return [x[len(mod) + 1:]
-                        for x in l if x.startswith(mod + '.' + name)]
+                return [x[len(mod) + 1 :] for x in l if x.startswith(mod + "." + name)]
         try:
             l = sorted(set(self.completer.complete(stem)))
             return l
         except (NameError, AttributeError):
             return []
 
+
 class ReaderConsole(code.InteractiveInterpreter):
     II_init = code.InteractiveInterpreter.__init__
+
     def __init__(self, console, locals=None):
         if locals is None:
             locals = {}
@@ -169,7 +184,7 @@ def __init__(self, console, locals=None):
         self.compiler = CommandCompiler()
         self.compile = self.compiler.compiler
         self.reader = PythonicReader(console, locals, self.compiler)
-        locals['Reader'] = self.reader
+        locals["Reader"] = self.reader
 
     def run_user_init_file(self):
         for key in "PYREPLSTARTUP", "PYTHONSTARTUP":
@@ -188,7 +203,7 @@ def run_user_init_file(self):
     def execute(self, text):
         try:
             # ooh, look at the hack:
-            code = self.compile(text, '<stdin>', 'single')
+            code = self.compile(text, "<stdin>", "single")
         except (OverflowError, SyntaxError, ValueError):
             self.showsyntaxerror("<stdin>")
         else:
@@ -198,13 +213,13 @@ def execute(self, text):
 
     def interact(self):
         while 1:
-            try: # catches EOFError's and KeyboardInterrupts during execution
-                try: # catches KeyboardInterrupts during editing
-                    try: # warning saver
+            try:  # catches EOFError's and KeyboardInterrupts during execution
+                try:  # catches KeyboardInterrupts during editing
+                    try:  # warning saver
                         # can't have warnings spewed onto terminal
                         sv = warnings.showwarning
                         warnings.showwarning = eat_it
-                        l = str(self.reader.readline(), 'utf-8')
+                        l = str(self.reader.readline(), "utf-8")
                     finally:
                         warnings.showwarning = sv
                 except KeyboardInterrupt:
@@ -221,7 +236,7 @@ def prepare(self):
         self.sv_sw = warnings.showwarning
         warnings.showwarning = eat_it
         self.reader.prepare()
-        self.reader.refresh() # we want :after methods...
+        self.reader.refresh()  # we want :after methods...
 
     def restore(self):
         self.reader.restore()
@@ -254,10 +269,11 @@ def tkfilehandler(self, file, mask):
     # createfilehandler)?  threads, I guess
     def really_tkinteract(self):
         import _tkinter
+
         _tkinter.createfilehandler(
-            self.reader.console.input_fd, _tkinter.READABLE,
-            self.tkfilehandler)
-        
+            self.reader.console.input_fd, _tkinter.READABLE, self.tkfilehandler
+        )
+
         self.exc_info = None
         while 1:
             # dooneevent will return 0 without blocking if there are
@@ -270,13 +286,13 @@ def really_tkinteract(self):
                 type, value, tb = self.exc_info
                 self.exc_info = None
                 _reraise(type, value, tb)
-        
+
     def tkinteract(self):
         """Run a Tk-aware Python interactive session.
 
         This function simulates the Python top-level in a way that
         allows Tk's mainloop to run."""
-        
+
         # attempting to understand the control flow of this function
         # without help may cause internal injuries.  so, some
         # explanation.
@@ -295,7 +311,7 @@ def tkinteract(self):
         # KeyboardInterrupts cause a restart.  All other exceptions
         # are likely bugs in pyrepl (well, 'cept for SystemExit, of
         # course).
-        
+
         while 1:
             try:
                 try:
@@ -318,13 +334,16 @@ def tkinteract(self):
                 break
 
     def twistedinteract(self):
+        import signal
+
         from twisted.internet import reactor
         from twisted.internet.abstract import FileDescriptor
-        import signal
+
         outerself = self
+
         class Me(FileDescriptor):
             def fileno(self):
-                """ We want to select on FD 0 """
+                """We want to select on FD 0"""
                 return 0
 
             def doRead(self):
@@ -335,67 +354,80 @@ def doRead(self):
                     reactor.stop()
 
         reactor.addReader(Me())
-        reactor.callWhenRunning(signal.signal,
-                                signal.SIGINT,
-                                signal.default_int_handler)
+        reactor.callWhenRunning(
+            signal.signal, signal.SIGINT, signal.default_int_handler
+        )
         self.prepare()
         try:
             reactor.run()
         finally:
             self.restore()
-        
 
     def cocoainteract(self, inputfilehandle=None, outputfilehandle=None):
         # only call this when there's a run loop already going!
         # note that unlike the other *interact methods, this returns immediately
         from cocoasupport import CocoaInteracter
-        self.cocoainteracter = CocoaInteracter.alloc().init(self, inputfilehandle, outputfilehandle)
-        
-        
-def main(use_pygame_console=0, interactmethod=default_interactmethod, print_banner=True, clear_main=True):
+
+        self.cocoainteracter = CocoaInteracter.alloc().init(
+            self, inputfilehandle, outputfilehandle
+        )
+
+
+def main(
+    use_pygame_console=0,
+    interactmethod=default_interactmethod,
+    print_banner=True,
+    clear_main=True,
+):
     si, se, so = sys.stdin, sys.stderr, sys.stdout
     try:
-        if 0 and use_pygame_console: # pygame currently borked
-            from pyrepl.pygame_console import PyGameConsole, FakeStdin, FakeStdout
+        if 0 and use_pygame_console:  # pygame currently borked
+            from pyrepl.pygame_console import (FakeStdin, FakeStdout,
+                                               PyGameConsole)
+
             con = PyGameConsole()
             sys.stderr = sys.stdout = FakeStdout(con)
             sys.stdin = FakeStdin(con)
         else:
             from pyrepl.unix_console import UnixConsole
+
             try:
                 import locale
             except ImportError:
                 encoding = None
             else:
-                if hasattr(locale, 'nl_langinfo') \
-                       and hasattr(locale, 'CODESET'):
+                if hasattr(locale, "nl_langinfo") and hasattr(locale, "CODESET"):
                     encoding = locale.nl_langinfo(locale.CODESET)
-                elif os.environ.get('TERM_PROGRAM') == 'Apple_Terminal':
+                elif os.environ.get("TERM_PROGRAM") == "Apple_Terminal":
                     # /me whistles innocently...
-                    code = int(os.popen(
-                        "defaults read com.apple.Terminal StringEncoding"
-                        ).read())
+                    code = int(
+                        os.popen(
+                            "defaults read com.apple.Terminal StringEncoding"
+                        ).read()
+                    )
                     if code == 4:
-                        encoding = 'utf-8'
+                        encoding = "utf-8"
                         # More could go here -- and what's here isn't
                         # bulletproof.  What would be?  AppleScript?
                         # Doesn't seem to be possible.
                     else:
                         encoding = None
                 else:
-                    encoding = None # so you get ASCII...
+                    encoding = None  # so you get ASCII...
             con = UnixConsole(os.dup(0), os.dup(1), None, encoding)
         if print_banner:
             print("Python", sys.version, "on", sys.platform)
-            print('Type "help", "copyright", "credits" or "license" '\
-                  'for more information.')
+            print(
+                'Type "help", "copyright", "credits" or "license" '
+                "for more information."
+            )
         sys.path.insert(0, os.getcwd())
 
-        if clear_main and __name__ != '__main__':
-            mainmod = imp.new_module('__main__')
-            sys.modules['__main__'] = mainmod
+        if clear_main and __name__ != "__main__":
+            mainmod = imp.new_module("__main__")
+            sys.modules["__main__"] = mainmod
         else:
-            mainmod = sys.modules['__main__']
+            mainmod = sys.modules["__main__"]
 
         rc = ReaderConsole(con, mainmod.__dict__)
         rc.reader._module_list_ready = False
@@ -404,5 +436,6 @@ def main(use_pygame_console=0, interactmethod=default_interactmethod, print_bann
     finally:
         sys.stdin, sys.stderr, sys.stdout = si, se, so
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     main()
diff --git a/pyrepl/reader.py b/pyrepl/reader.py
index 5d4c727..c79ba66 100644
--- a/pyrepl/reader.py
+++ b/pyrepl/reader.py
@@ -21,8 +21,9 @@
 
 
 import unicodedata
-from pyrepl import commands
-from pyrepl import input
+
+from pyrepl import commands, input
+
 try:
     str
 except NameError:
@@ -34,17 +35,17 @@
 def _make_unctrl_map():
     uc_map = {}
     for c in map(chr, list(range(256))):
-        if unicodedata.category(c)[0] != 'C':
+        if unicodedata.category(c)[0] != "C":
             uc_map[c] = c
     for i in range(32):
         c = chr(i)
-        uc_map[c] = '^' + chr(ord('A') + i - 1)
-    uc_map[b'\t'] = '    '  # display TABs as 4 characters
-    uc_map[b'\177'] = str('^?')
+        uc_map[c] = "^" + chr(ord("A") + i - 1)
+    uc_map[b"\t"] = "    "  # display TABs as 4 characters
+    uc_map[b"\177"] = str("^?")
     for i in range(256):
         c = chr(i)
         if c not in uc_map:
-            uc_map[c] = str('\\%03o') % i
+            uc_map[c] = str("\\%03o") % i
     return uc_map
 
 
@@ -52,14 +53,14 @@ def _my_unctrl(c, u=_make_unctrl_map()):
     if c in u:
         return u[c]
     else:
-        if unicodedata.category(c).startswith('C'):
-            return br'\u%04x' % ord(c)
+        if unicodedata.category(c).startswith("C"):
+            return rb"\u%04x" % ord(c)
         else:
             return c
 
 
-def disp_str(buffer, join=''.join, uc=_my_unctrl):
-    """ disp_str(buffer:string) -> (string, [int])
+def disp_str(buffer, join="".join, uc=_my_unctrl):
+    """disp_str(buffer:string) -> (string, [int])
 
     Return the string that should be the printed represenation of
     |buffer| and a list detailing where the characters of |buffer|
@@ -79,15 +80,14 @@ def disp_str(buffer, join=''.join, uc=_my_unctrl):
         b.extend([0] * (len(x) - 1))
     return join(s), b
 
+
 del _my_unctrl
 
 del _make_unctrl_map
 
 # syntax classes:
 
-[SYNTAX_WHITESPACE,
- SYNTAX_WORD,
- SYNTAX_SYMBOL] = list(range(3))
+[SYNTAX_WHITESPACE, SYNTAX_WORD, SYNTAX_SYMBOL] = list(range(3))
 
 
 def make_default_syntax_table():
@@ -97,76 +97,78 @@ def make_default_syntax_table():
         st[c] = SYNTAX_SYMBOL
     for c in [a for a in map(chr, list(range(256))) if a.isalpha()]:
         st[c] = SYNTAX_WORD
-    st[str('\n')] = st[str(' ')] = SYNTAX_WHITESPACE
+    st[str("\n")] = st[str(" ")] = SYNTAX_WHITESPACE
     return st
 
+
 default_keymap = tuple(
-    [(r'\C-a', 'beginning-of-line'),
-     (r'\C-b', 'left'),
-     (r'\C-c', 'interrupt'),
-     (r'\C-d', 'delete'),
-     (r'\C-e', 'end-of-line'),
-     (r'\C-f', 'right'),
-     (r'\C-g', 'cancel'),
-     (r'\C-h', 'backspace'),
-     (r'\C-j', 'accept'),
-     (r'\<return>', 'accept'),
-     (r'\C-k', 'kill-line'),
-     (r'\C-l', 'clear-screen'),
-     (r'\C-m', 'accept'),
-     (r'\C-q', 'quoted-insert'),
-     (r'\C-t', 'transpose-characters'),
-     (r'\C-u', 'unix-line-discard'),
-     (r'\C-v', 'quoted-insert'),
-     (r'\C-w', 'unix-word-rubout'),
-     (r'\C-x\C-u', 'upcase-region'),
-     (r'\C-y', 'yank'),
-     (r'\C-z', 'suspend'),
-
-     (r'\M-b', 'backward-word'),
-     (r'\M-c', 'capitalize-word'),
-     (r'\M-d', 'kill-word'),
-     (r'\M-f', 'forward-word'),
-     (r'\M-l', 'downcase-word'),
-     (r'\M-t', 'transpose-words'),
-     (r'\M-u', 'upcase-word'),
-     (r'\M-y', 'yank-pop'),
-     (r'\M--', 'digit-arg'),
-     (r'\M-0', 'digit-arg'),
-     (r'\M-1', 'digit-arg'),
-     (r'\M-2', 'digit-arg'),
-     (r'\M-3', 'digit-arg'),
-     (r'\M-4', 'digit-arg'),
-     (r'\M-5', 'digit-arg'),
-     (r'\M-6', 'digit-arg'),
-     (r'\M-7', 'digit-arg'),
-     (r'\M-8', 'digit-arg'),
-     (r'\M-9', 'digit-arg'),
-     #(r'\M-\n', 'insert-nl'),
-     ('\\\\', 'self-insert')] +
-    [(c, 'self-insert')
-     for c in map(chr, list(range(32, 127))) if c != '\\'] +
-    [(c, 'self-insert')
-     for c in map(chr, list(range(128, 256))) if c.isalpha()] +
-    [(r'\<up>', 'up'),
-     (r'\<down>', 'down'),
-     (r'\<left>', 'left'),
-     (r'\<right>', 'right'),
-     (r'\<insert>', 'quoted-insert'),
-     (r'\<delete>', 'delete'),
-     (r'\<backspace>', 'backspace'),
-     (r'\M-\<backspace>', 'backward-kill-word'),
-     (r'\<end>', 'end-of-line'),         # was 'end'
-     (r'\<home>', 'beginning-of-line'),  # was 'home'
-     (r'\<f1>', 'help'),
-     (r'\EOF', 'end'),   # the entries in the terminfo database for xterms
-     (r'\EOH', 'home'),  # seem to be wrong.  this is a less than ideal
-                         # workaround
-     (r'\<ctrl left>',  'backward-word'),
-     (r'\<ctrl right>', 'forward-word'),
-     ])
-
-if 'c' in globals():  # only on python 2.x
+    [
+        (r"\C-a", "beginning-of-line"),
+        (r"\C-b", "left"),
+        (r"\C-c", "interrupt"),
+        (r"\C-d", "delete"),
+        (r"\C-e", "end-of-line"),
+        (r"\C-f", "right"),
+        (r"\C-g", "cancel"),
+        (r"\C-h", "backspace"),
+        (r"\C-j", "accept"),
+        (r"\<return>", "accept"),
+        (r"\C-k", "kill-line"),
+        (r"\C-l", "clear-screen"),
+        (r"\C-m", "accept"),
+        (r"\C-q", "quoted-insert"),
+        (r"\C-t", "transpose-characters"),
+        (r"\C-u", "unix-line-discard"),
+        (r"\C-v", "quoted-insert"),
+        (r"\C-w", "unix-word-rubout"),
+        (r"\C-x\C-u", "upcase-region"),
+        (r"\C-y", "yank"),
+        (r"\C-z", "suspend"),
+        (r"\M-b", "backward-word"),
+        (r"\M-c", "capitalize-word"),
+        (r"\M-d", "kill-word"),
+        (r"\M-f", "forward-word"),
+        (r"\M-l", "downcase-word"),
+        (r"\M-t", "transpose-words"),
+        (r"\M-u", "upcase-word"),
+        (r"\M-y", "yank-pop"),
+        (r"\M--", "digit-arg"),
+        (r"\M-0", "digit-arg"),
+        (r"\M-1", "digit-arg"),
+        (r"\M-2", "digit-arg"),
+        (r"\M-3", "digit-arg"),
+        (r"\M-4", "digit-arg"),
+        (r"\M-5", "digit-arg"),
+        (r"\M-6", "digit-arg"),
+        (r"\M-7", "digit-arg"),
+        (r"\M-8", "digit-arg"),
+        (r"\M-9", "digit-arg"),
+        # (r'\M-\n', 'insert-nl'),
+        ("\\\\", "self-insert"),
+    ]
+    + [(c, "self-insert") for c in map(chr, list(range(32, 127))) if c != "\\"]
+    + [(c, "self-insert") for c in map(chr, list(range(128, 256))) if c.isalpha()]
+    + [
+        (r"\<up>", "up"),
+        (r"\<down>", "down"),
+        (r"\<left>", "left"),
+        (r"\<right>", "right"),
+        (r"\<insert>", "quoted-insert"),
+        (r"\<delete>", "delete"),
+        (r"\<backspace>", "backspace"),
+        (r"\M-\<backspace>", "backward-kill-word"),
+        (r"\<end>", "end-of-line"),  # was 'end'
+        (r"\<home>", "beginning-of-line"),  # was 'home'
+        (r"\<f1>", "help"),
+        (r"\EOF", "end"),  # the entries in the terminfo database for xterms
+        (r"\EOH", "home"),  # seem to be wrong.  this is a less than ideal
+        # workaround
+        (r"\<ctrl left>", "backward-word"),
+        (r"\<ctrl right>", "forward-word"),
+    ]
+)
+
+if "c" in globals():  # only on python 2.x
     del c  # from the listcomps
 
 
@@ -244,20 +246,21 @@ def __init__(self, console):
         self.finished = 0
         self.console = console
         self.commands = {}
-        self.msg = ''
+        self.msg = ""
         for v in list(vars(commands).values()):
-            if (isinstance(v, type) and
-                    issubclass(v, commands.Command) and
-                    v.__name__[0].islower()):
+            if (
+                isinstance(v, type)
+                and issubclass(v, commands.Command)
+                and v.__name__[0].islower()
+            ):
                 self.commands[v.__name__] = v
-                self.commands[v.__name__.replace('_', '-')] = v
+                self.commands[v.__name__.replace("_", "-")] = v
         self.syntax_table = make_default_syntax_table()
         self.input_trans_stack = []
         self.keymap = self.collect_keymap()
         self.input_trans = input.KeymapTranslator(
-            self.keymap,
-            invalid_cls='invalid-key',
-            character_cls='self-insert')
+            self.keymap, invalid_cls="invalid-key", character_cls="self-insert"
+        )
 
     def collect_keymap(self):
         return default_keymap
@@ -282,8 +285,8 @@ def calc_screen(self):
                         screeninfo.append((0, []))
                 self.lxy = p, ln
             prompt = self.get_prompt(ln, ll >= p >= 0)
-            while '\n' in prompt:
-                pre_prompt, _, prompt = prompt.partition('\n')
+            while "\n" in prompt:
+                pre_prompt, _, prompt = prompt.partition("\n")
                 screen.append(pre_prompt)
                 screeninfo.append((0, []))
             p -= ll + 1
@@ -294,13 +297,13 @@ def calc_screen(self):
                 screen.append(prompt + l)
                 screeninfo.append((lp, l2 + [1]))
             else:
-                screen.append(prompt + l[:w - lp] + "\\")
-                screeninfo.append((lp, l2[:w - lp]))
+                screen.append(prompt + l[: w - lp] + "\\")
+                screeninfo.append((lp, l2[: w - lp]))
                 for i in range(-lp + w, -lp + wrapcount * w, w):
-                    screen.append(l[i:i + w] + "\\")
-                    screeninfo.append((0, l2[i:i + w]))
-                screen.append(l[wrapcount * w - lp:])
-                screeninfo.append((0, l2[wrapcount * w - lp:] + [1]))
+                    screen.append(l[i : i + w] + "\\")
+                    screeninfo.append((0, l2[i : i + w]))
+                screen.append(l[wrapcount * w - lp :])
+                screeninfo.append((0, l2[wrapcount * w - lp :] + [1]))
         self.screeninfo = screeninfo
         self.cxy = self.pos2xy(self.pos)
         if self.msg and self.msg_at_bottom:
@@ -310,26 +313,26 @@ def calc_screen(self):
         return screen
 
     def process_prompt(self, prompt):
-        """ Process the prompt.
+        """Process the prompt.
 
         This means calculate the length of the prompt. The character \x01
         and \x02 are used to bracket ANSI control sequences and need to be
         excluded from the length calculation.  So also a copy of the prompt
-        is returned with these control characters removed.  """
+        is returned with these control characters removed."""
 
-        out_prompt = ''
+        out_prompt = ""
         l = len(prompt)
         pos = 0
         while True:
-            s = prompt.find('\x01', pos)
+            s = prompt.find("\x01", pos)
             if s == -1:
                 break
-            e = prompt.find('\x02', s)
+            e = prompt.find("\x02", s)
             if e == -1:
                 break
             # Found start and end brackets, subtract from string length
             l = l - (e - s + 1)
-            out_prompt += prompt[pos:s] + prompt[s + 1:e]
+            out_prompt += prompt[pos:s] + prompt[s + 1 : e]
             pos = e + 1
         out_prompt += prompt[pos:]
         return out_prompt, l
@@ -377,7 +380,7 @@ def bol(self, p=None):
             p = self.pos
         b = self.buffer
         p -= 1
-        while p >= 0 and b[p] != '\n':
+        while p >= 0 and b[p] != "\n":
             p -= 1
         return p + 1
 
@@ -389,7 +392,7 @@ def eol(self, p=None):
         if p is None:
             p = self.pos
         b = self.buffer
-        while p < len(b) and b[p] != '\n':
+        while p < len(b) and b[p] != "\n":
             p += 1
         return p
 
@@ -458,7 +461,7 @@ def pos2xy(self, pos):
 
     def insert(self, text):
         """Insert 'text' at the insertion point."""
-        self.buffer[self.pos:self.pos] = list(text)
+        self.buffer[self.pos : self.pos] = list(text)
         self.pos += len(text)
         self.dirty = 1
 
@@ -522,11 +525,10 @@ def refresh(self):
         self.dirty = 0  # forgot this for a while (blush)
 
     def do_cmd(self, cmd):
-        #print cmd
+        # print cmd
         if isinstance(cmd[0], str):
-            #XXX: unify to text
-            cmd = self.commands.get(cmd[0],
-                                    commands.invalid_command)(self, *cmd)
+            # XXX: unify to text
+            cmd = self.commands.get(cmd[0], commands.invalid_command)(self, *cmd)
         elif isinstance(cmd[0], type):
             cmd = cmd[0](self, *cmd)
         else:
@@ -555,7 +557,7 @@ def handle1(self, block=1):
         pending."""
 
         if self.msg:
-            self.msg = ''
+            self.msg = ""
             self.dirty = 1
 
         while 1:
@@ -565,11 +567,11 @@ def handle1(self, block=1):
 
             translate = True
 
-            if event.evt == 'key':
+            if event.evt == "key":
                 self.input_trans.push(event)
-            elif event.evt == 'scroll':
+            elif event.evt == "scroll":
                 self.refresh()
-            elif event.evt == 'resize':
+            elif event.evt == "resize":
                 self.refresh()
             else:
                 translate = False
@@ -612,22 +614,22 @@ def readline(self, returns_unicode=False, startup_hook=None):
     def bind(self, spec, command):
         self.keymap = self.keymap + ((spec, command),)
         self.input_trans = input.KeymapTranslator(
-            self.keymap,
-            invalid_cls='invalid-key',
-            character_cls='self-insert')
+            self.keymap, invalid_cls="invalid-key", character_cls="self-insert"
+        )
 
     def get_buffer(self, encoding=None):
         if encoding is None:
             encoding = self.console.encoding
-        return str('').join(self.buffer).encode(self.console.encoding)
+        return str("").join(self.buffer).encode(self.console.encoding)
 
     def get_unicode(self):
         """Return the current buffer as a unicode string."""
-        return str('').join(self.buffer)
+        return str("").join(self.buffer)
 
 
 def test():
     from pyrepl.unix_console import UnixConsole
+
     reader = Reader(UnixConsole())
     reader.ps1 = "**> "
     reader.ps2 = "/*> "
@@ -637,5 +639,5 @@ def test():
         pass
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test()
diff --git a/pyrepl/readline.py b/pyrepl/readline.py
index e9022cc..a4869df 100644
--- a/pyrepl/readline.py
+++ b/pyrepl/readline.py
@@ -26,50 +26,42 @@
 extensions for multiline input.
 """
 
-import sys
 import os
+import sys
+
 from pyrepl import commands
-from pyrepl.historical_reader import HistoricalReader
 from pyrepl.completing_reader import CompletingReader
+from pyrepl.historical_reader import HistoricalReader
 from pyrepl.unix_console import UnixConsole, _error
-try:
-    str
-    PY3 = False
-except NameError:
-    PY3 = True
-    str = str
-    chr = chr
-    str = bytes, str
-
 
-ENCODING = sys.getfilesystemencoding() or 'latin1'     # XXX review
+ENCODING = sys.getfilesystemencoding() or "latin1"  # XXX review
 
 __all__ = [
-    'add_history',
-    'clear_history',
-    'get_begidx',
-    'get_completer',
-    'get_completer_delims',
-    'get_current_history_length',
-    'get_endidx',
-    'get_history_item',
-    'get_history_length',
-    'get_line_buffer',
-    'insert_text',
-    'parse_and_bind',
-    'read_history_file',
-    'read_init_file',
-    'redisplay',
-    'remove_history_item',
-    'replace_history_item',
-    'set_completer',
-    'set_completer_delims',
-    'set_history_length',
-    'set_pre_input_hook',
-    'set_startup_hook',
-    'write_history_file',
+    "add_history",
+    "clear_history",
+    "get_begidx",
+    "get_completer",
+    "get_completer_delims",
+    "get_current_history_length",
+    "get_endidx",
+    "get_history_item",
+    "get_history_length",
+    "get_line_buffer",
+    "insert_text",
+    "parse_and_bind",
+    "read_history_file",
+    "read_init_file",
+    "redisplay",
+    "remove_history_item",
+    "replace_history_item",
+    "set_completer",
+    "set_completer_delims",
+    "set_history_length",
+    "set_pre_input_hook",
+    "set_startup_hook",
+    "write_history_file",
     # ---- multiline extensions ----
-    'multiline_input',
+    "multiline_input",
 ]
 
 # ____________________________________________________________
@@ -77,7 +69,7 @@
 
 class ReadlineConfig(object):
     readline_completer = None
-    completer_delims = dict.fromkeys(' \t\n`~!@#$%^&*()-=+[{]}\\|;:\'",<>/?')
+    completer_delims = dict.fromkeys(" \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>/?")
 
 
 class ReadlineAlikeReader(HistoricalReader, CompletingReader):
@@ -86,7 +78,7 @@ class ReadlineAlikeReader(HistoricalReader, CompletingReader):
     sort_in_column = True
 
     def error(self, msg="none"):
-        pass    # don't show error messages by default
+        pass  # don't show error messages by default
 
     def get_stem(self):
         b = self.buffer
@@ -94,16 +86,16 @@ def get_stem(self):
         completer_delims = self.config.completer_delims
         while p >= 0 and b[p] not in completer_delims:
             p -= 1
-        return ''.join(b[p+1:self.pos])
+        return "".join(b[p + 1 : self.pos])
 
     def get_completions(self, stem):
         result = []
         function = self.config.readline_completer
         if function is not None:
             try:
-                stem = str(stem)   # rlcompleter.py seems to not like unicode
+                stem = str(stem)  # rlcompleter.py seems to not like unicode
             except UnicodeEncodeError:
-                pass   # but feed unicode anyway if we have no choice
+                pass  # but feed unicode anyway if we have no choice
             state = 0
             while True:
                 try:
@@ -144,12 +136,13 @@ def get_trimmed_history(self, maxlength):
 
     def collect_keymap(self):
         return super(ReadlineAlikeReader, self).collect_keymap() + (
-            (r'\n', 'maybe-accept'),)
+            (r"\n", "maybe-accept"),
+        )
 
     def __init__(self, console):
         super(ReadlineAlikeReader, self).__init__(console)
-        self.commands['maybe_accept'] = maybe_accept
-        self.commands['maybe-accept'] = maybe_accept
+        self.commands["maybe_accept"] = maybe_accept
+        self.commands["maybe-accept"] = maybe_accept
 
     def after_command(self, cmd):
         super(ReadlineAlikeReader, self).after_command(cmd)
@@ -176,7 +169,7 @@ def do(self):
         # if there are already several lines and the cursor
         # is not on the last one, always insert a new \n.
         text = r.get_unicode()
-        if "\n" in r.buffer[r.pos:]:
+        if "\n" in r.buffer[r.pos :]:
             r.insert("\n")
         elif r.more_lines is not None and r.more_lines(text):
             r.insert("\n")
@@ -209,7 +202,7 @@ def get_reader(self):
             self.reader.config = self.config
         return self.reader
 
-    def raw_input(self, prompt=''):
+    def raw_input(self, prompt=""):
         try:
             reader = self.get_reader()
         except _error:
@@ -221,9 +214,9 @@ def raw_input(self, prompt=''):
         # behavior: it seems to be the correct thing to do, and moreover it
         # mitigates this pytest issue:
         # https://github.com/pytest-dev/pytest/issues/5134
-        if self.stdout and hasattr(self.stdout, 'flush'):
+        if self.stdout and hasattr(self.stdout, "flush"):
             self.stdout.flush()
-        if self.stderr and hasattr(self.stderr, 'flush'):
+        if self.stderr and hasattr(self.stderr, "flush"):
             self.stderr.flush()
 
         ret = reader.readline(startup_hook=self.startup_hook)
@@ -264,17 +257,17 @@ def set_completer_delims(self, string):
     def get_completer_delims(self):
         chars = list(self.config.completer_delims.keys())
         chars.sort()
-        return ''.join(chars)
+        return "".join(chars)
 
     def _histline(self, line):
-        line = line.rstrip('\n')
+        line = line.rstrip("\n")
         if PY3:
             return line
 
         try:
             return str(line, ENCODING)
-        except UnicodeDecodeError:   # bah, silently fall back...
-            return str(line, 'utf-8', 'replace')
+        except UnicodeDecodeError:  # bah, silently fall back...
+            return str(line, "utf-8", "replace")
 
     def get_history_length(self):
         return self.saved_history_length
@@ -285,43 +278,43 @@ def set_history_length(self, length):
     def get_current_history_length(self):
         return len(self.get_reader().history)
 
-    def read_history_file(self, filename='~/.history'):
+    def read_history_file(self, filename="~/.history"):
         # multiline extension (really a hack) for the end of lines that
         # are actually continuations inside a single multiline_input()
         # history item: we use \r\n instead of just \n.  If the history
         # file is passed to GNU readline, the extra \r are just ignored.
         history = self.get_reader().history
-        f = open(os.path.expanduser(filename), 'r')
+        f = open(os.path.expanduser(filename), "r")
         buffer = []
         for line in f:
-            if line.endswith('\r\n'):
+            if line.endswith("\r\n"):
                 buffer.append(line)
             else:
                 line = self._histline(line)
                 if buffer:
-                    line = ''.join(buffer).replace('\r', '') + line
+                    line = "".join(buffer).replace("\r", "") + line
                     del buffer[:]
                 if line:
                     history.append(line)
         f.close()
 
-    def write_history_file(self, filename='~/.history'):
+    def write_history_file(self, filename="~/.history"):
         maxlength = self.saved_history_length
         history = self.get_reader().get_trimmed_history(maxlength)
-        entries = ''
+        entries = ""
         for entry in history:
             # if we are on py3k, we don't need to encode strings before
             # writing it to a file
             if isinstance(entry, str) and sys.version_info < (3,):
-                entry = entry.encode('utf-8')
-            entry = entry.replace('\n', '\r\n')   # multiline history support
-            entries += entry + '\n'
+                entry = entry.encode("utf-8")
+            entry = entry.replace("\n", "\r\n")  # multiline history support
+            entries += entry + "\n"
 
         fname = os.path.expanduser(filename)
         if PY3:
-            f = open(fname, 'w', encoding='utf-8')
+            f = open(fname, "w", encoding="utf-8")
         else:
-            f = open(fname, 'w')
+            f = open(fname, "w")
         f.write(entries)
         f.close()
 
@@ -331,9 +324,9 @@ def clear_history(self):
     def get_history_item(self, index):
         history = self.get_reader().history
         if 1 <= index <= len(history):
-            return history[index-1]
+            return history[index - 1]
         else:
-            return None        # blame readline.c for not raising
+            return None  # blame readline.c for not raising
 
     def remove_history_item(self, index):
         history = self.get_reader().history
@@ -420,14 +413,17 @@ def insert_text(self, text):
 def _make_stub(_name, _ret):
     def stub(*args, **kwds):
         import warnings
+
         warnings.warn("readline.%s() not implemented" % _name, stacklevel=2)
+
     stub.__name__ = _name
     globals()[_name] = stub
 
+
 for _name, _ret in [
-    ('read_init_file', None),
-    ('redisplay', None),
-    ('set_pre_input_hook', None),
+    ("read_init_file", None),
+    ("redisplay", None),
+    ("set_pre_input_hook", None),
 ]:
     assert _name not in globals(), _name
     _make_stub(_name, _ret)
@@ -451,9 +447,9 @@ def _setup():
     _wrapper.f_out = f_out
     _wrapper.setup_std_streams(sys.stdin, sys.stdout, sys.stderr)
 
-    if '__pypy__' in sys.builtin_module_names:    # PyPy
+    if "__pypy__" in sys.builtin_module_names:  # PyPy
 
-        def _old_raw_input(prompt=''):
+        def _old_raw_input(prompt=""):
             # sys.__raw_input__() is only called when stdin and stdout are
             # as expected and are ttys.  If it is the case, then get_reader()
             # should not really fail in _wrapper.raw_input().  If it still
@@ -464,18 +460,22 @@ def _old_raw_input(prompt=''):
             except AttributeError:
                 pass
             return input(prompt)
+
         sys.__raw_input__ = _wrapper.raw_input
 
     else:
         # this is not really what readline.c does.  Better than nothing I guess
         try:
             import builtins
+
             _old_raw_input = builtins.raw_input
             builtins.raw_input = _wrapper.raw_input
         except ImportError:
             import builtins
+
             _old_raw_input = builtins.input
             builtins.input = _wrapper.raw_input
 
+
 _old_raw_input = None
 _setup()
diff --git a/pyrepl/simple_interact.py b/pyrepl/simple_interact.py
index 3b84a15..bb3e1ce 100644
--- a/pyrepl/simple_interact.py
+++ b/pyrepl/simple_interact.py
@@ -24,10 +24,11 @@
 """
 
 import sys
-from pyrepl.readline import multiline_input, _error, _get_reader
 
+from pyrepl.readline import _error, _get_reader, multiline_input
 
-def check():     # returns False if there is a problem initializing the state
+
+def check():  # returns False if there is a problem initializing the state
     try:
         _get_reader()
     except _error:
@@ -37,20 +38,22 @@ def check():     # returns False if there is a problem initializing the state
 
 def run_multiline_interactive_console(mainmodule=None, future_flags=0):
     import code
+
     import __main__
+
     mainmodule = mainmodule or __main__
-    console = code.InteractiveConsole(mainmodule.__dict__, filename='<stdin>')
+    console = code.InteractiveConsole(mainmodule.__dict__, filename="<stdin>")
     if future_flags:
         console.compile.compiler.flags |= future_flags
 
     def more_lines(unicodetext):
-        if sys.version_info < (3, ):
+        if sys.version_info < (3,):
             # ooh, look at the hack:
-            src = "#coding:utf-8\n"+unicodetext.encode('utf-8')
+            src = "#coding:utf-8\n" + unicodetext.encode("utf-8")
         else:
             src = unicodetext
         try:
-            code = console.compile(src, '<stdin>', 'single')
+            code = console.compile(src, "<stdin>", "single")
         except (OverflowError, SyntaxError, ValueError):
             return False
         else:
@@ -58,11 +61,10 @@ def more_lines(unicodetext):
 
     while 1:
         try:
-            ps1 = getattr(sys, 'ps1', '>>> ')
-            ps2 = getattr(sys, 'ps2', '... ')
+            ps1 = getattr(sys, "ps1", ">>> ")
+            ps2 = getattr(sys, "ps2", "... ")
             try:
-                statement = multiline_input(more_lines, ps1, ps2,
-                                            returns_unicode=True)
+                statement = multiline_input(more_lines, ps1, ps2, returns_unicode=True)
             except EOFError:
                 break
             more = console.push(statement)
diff --git a/pyrepl/trace.py b/pyrepl/trace.py
index 8b0dd35..f141660 100644
--- a/pyrepl/trace.py
+++ b/pyrepl/trace.py
@@ -3,15 +3,15 @@
 trace_filename = os.environ.get("PYREPL_TRACE")
 
 if trace_filename is not None:
-    trace_file = open(trace_filename, 'a')
+    trace_file = open(trace_filename, "a")
 else:
     trace_file = None
 
+
 def trace(line, *k, **kw):
     if trace_file is None:
         return
     if k or kw:
         line = line.format(*k, **kw)
-    trace_file.write(line+'\n')
+    trace_file.write(line + "\n")
     trace_file.flush()
-
diff --git a/pyrepl/unix_console.py b/pyrepl/unix_console.py
index 39fff7c..b7fb3e6 100644
--- a/pyrepl/unix_console.py
+++ b/pyrepl/unix_console.py
@@ -19,26 +19,28 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-import termios
-import select
-import os
-import struct
 import errno
-import signal
+import os
 import re
-import time
+import select
+import signal
+import struct
 import sys
+import termios
+import time
 from fcntl import ioctl
+
 from . import curses
-from .fancy_termios import tcgetattr, tcsetattr
 from .console import Console, Event
-from .unix_eventqueue import EventQueue
+from .fancy_termios import tcgetattr, tcsetattr
 from .trace import trace
+from .unix_eventqueue import EventQueue
 
 
 class InvalidTerminal(RuntimeError):
     pass
 
+
 try:
     str
 except NameError:
@@ -47,7 +49,7 @@ class InvalidTerminal(RuntimeError):
 _error = (termios.error, curses.error, InvalidTerminal)
 
 # there are arguments for changing this to "refresh"
-SIGWINCH_EVENT = 'repaint'
+SIGWINCH_EVENT = "repaint"
 
 FIONREAD = getattr(termios, "FIONREAD", None)
 TIOCGWINSZ = getattr(termios, "TIOCGWINSZ", None)
@@ -57,19 +59,41 @@ def _my_getstr(cap, optional=0):
     r = curses.tigetstr(cap)
     if not optional and r is None:
         raise InvalidTerminal(
-            "terminal doesn't have the required '%s' capability" % cap)
+            "terminal doesn't have the required '%s' capability" % cap
+        )
     return r
 
 
 # at this point, can we say: AAAAAAAAAAAAAAAAAAAAAARGH!
 def maybe_add_baudrate(dict, rate):
-    name = 'B%d' % rate
+    name = "B%d" % rate
     if hasattr(termios, name):
         dict[getattr(termios, name)] = rate
 
+
 ratedict = {}
-for r in [0, 110, 115200, 1200, 134, 150, 1800, 19200, 200, 230400,
-          2400, 300, 38400, 460800, 4800, 50, 57600, 600, 75, 9600]:
+for r in [
+    0,
+    110,
+    115200,
+    1200,
+    134,
+    150,
+    1800,
+    19200,
+    200,
+    230400,
+    2400,
+    300,
+    38400,
+    460800,
+    4800,
+    50,
+    57600,
+    600,
+    75,
+    9600,
+]:
     maybe_add_baudrate(ratedict, r)
 
 del r, maybe_add_baudrate
@@ -92,13 +116,15 @@ def poll(self, timeout=None):
             r, w, e = select.select([self.fd], [], [], timeout)
             return r
 
+
 POLLIN = getattr(select, "POLLIN", None)
 
 
-required_curses_tistrings = 'bel clear cup el'
+required_curses_tistrings = "bel clear cup el"
 optional_curses_tistrings = (
-    'civis cnorm cub cub1 cud cud1 cud cud1 cuf '
-    'cuf1 cuu cuu1 dch dch1 hpa ich ich1 ind pad ri rmkx smkx')
+    "civis cnorm cub cub1 cud cud1 cud cud1 cuf "
+    "cuf1 cuu cuu1 dch dch1 hpa ich ich1 ind pad ri rmkx smkx"
+)
 
 
 class UnixConsole(Console):
@@ -124,10 +150,10 @@ def __init__(self, f_in=0, f_out=1, term=None, encoding=None):
         self.term = term
 
         for name in required_curses_tistrings.split():
-            setattr(self, '_' + name, _my_getstr(name))
+            setattr(self, "_" + name, _my_getstr(name))
 
         for name in optional_curses_tistrings.split():
-            setattr(self, '_' + name, _my_getstr(name, optional=1))
+            setattr(self, "_" + name, _my_getstr(name, optional=1))
 
         ## work out how we're going to sling the cursor around
         # hpa don't work in windows telnet :-(
@@ -198,8 +224,8 @@ def refresh(self, screen, c_xy):
             offset = max(len(screen) - height, 0)
             screen.append("")
 
-        oldscr = self.screen[old_offset:old_offset + height]
-        newscr = screen[offset:offset + height]
+        oldscr = self.screen[old_offset : old_offset + height]
+        newscr = screen[offset : offset + height]
 
         # use hardware scrolling if we have it.
         if old_offset > offset and self._ri:
@@ -221,9 +247,11 @@ def refresh(self, screen, c_xy):
 
         self.__offset = offset
 
-        for y, oldline, newline, in zip(list(range(offset, offset + height)),
-                                        oldscr,
-                                        newscr):
+        for (
+            y,
+            oldline,
+            newline,
+        ) in zip(list(range(offset, offset + height)), oldscr, newscr):
             if oldline != newline:
                 self.__write_changed_line(y, oldline, newline, px)
 
@@ -252,24 +280,31 @@ def __write_changed_line(self, y, oldline, newline, px):
         # reuse the oldline as much as possible, but stop as soon as we
         # encounter an ESCAPE, because it might be the start of an escape
         # sequene
-        #XXX unicode check!
-        while x < minlen and oldline[x] == newline[x] and newline[x] != '\x1b':
+        # XXX unicode check!
+        while x < minlen and oldline[x] == newline[x] and newline[x] != "\x1b":
             x += 1
-        if oldline[x:] == newline[x+1:] and self.ich1:
-            if (y == self.__posxy[1] and x > self.__posxy[0] and
-                    oldline[px:x] == newline[px+1:x+1]):
+        if oldline[x:] == newline[x + 1 :] and self.ich1:
+            if (
+                y == self.__posxy[1]
+                and x > self.__posxy[0]
+                and oldline[px:x] == newline[px + 1 : x + 1]
+            ):
                 x = px
             self.__move(x, y)
             self.__write_code(self.ich1)
             self.__write(newline[x])
             self.__posxy = x + 1, y
-        elif x < minlen and oldline[x + 1:] == newline[x + 1:]:
+        elif x < minlen and oldline[x + 1 :] == newline[x + 1 :]:
             self.__move(x, y)
             self.__write(newline[x])
             self.__posxy = x + 1, y
-        elif (self.dch1 and self.ich1 and len(newline) == self.width
-              and x < len(newline) - 2
-              and newline[x+1:-1] == oldline[x:-2]):
+        elif (
+            self.dch1
+            and self.ich1
+            and len(newline) == self.width
+            and x < len(newline) - 2
+            and newline[x + 1 : -1] == oldline[x:-2]
+        ):
             self.__hide_cursor()
             self.__move(self.width - 2, y)
             self.__posxy = self.width - 2, y
@@ -286,8 +321,8 @@ def __write_changed_line(self, y, oldline, newline, px):
             self.__write(newline[x:])
             self.__posxy = len(newline), y
 
-        #XXX: check for unicode mess
-        if '\x1b' in newline:
+        # XXX: check for unicode mess
+        if "\x1b" in newline:
             # ANSI escape characters are present, so we can't assume
             # anything about the position of the cursor.  Moving the cursor
             # to the left margin should work to get to a known position.
@@ -297,7 +332,6 @@ def __write(self, text):
         self.__buffer.append((text, 0))
 
     def __write_code(self, fmt, *args):
-
         self.__buffer.append((curses.tparm(fmt, *args), 1))
 
     def __maybe_write_code(self, fmt, *args):
@@ -307,9 +341,9 @@ def __maybe_write_code(self, fmt, *args):
     def __move_y_cuu1_cud1(self, y):
         dy = y - self.__posxy[1]
         if dy > 0:
-            self.__write_code(dy*self._cud1)
+            self.__write_code(dy * self._cud1)
         elif dy < 0:
-            self.__write_code((-dy)*self._cuu1)
+            self.__write_code((-dy) * self._cuu1)
 
     def __move_y_cuu_cud(self, y):
         dy = y - self.__posxy[1]
@@ -325,9 +359,9 @@ def __move_x_hpa(self, x):
     def __move_x_cub1_cuf1(self, x):
         dx = x - self.__posxy[0]
         if dx > 0:
-            self.__write_code(self._cuf1*dx)
+            self.__write_code(self._cuf1 * dx)
         elif dx < 0:
-            self.__write_code(self._cub1*(-dx))
+            self.__write_code(self._cub1 * (-dx))
 
     def __move_x_cub_cuf(self, x):
         dx = x - self.__posxy[0]
@@ -346,7 +380,7 @@ def __move_tall(self, x, y):
 
     def move_cursor(self, x, y):
         if y < self.__offset or y >= self.__offset + self.height:
-            self.event_queue.insert(Event('scroll', None))
+            self.event_queue.insert(Event("scroll", None))
         else:
             self.__move(x, y)
             self.__posxy = x, y
@@ -357,12 +391,12 @@ def prepare(self):
         self.__svtermstate = tcgetattr(self.input_fd)
         raw = self.__svtermstate.copy()
         raw.iflag |= termios.ICRNL
-        raw.iflag &= ~(termios.BRKINT | termios.INPCK |
-                       termios.ISTRIP | termios.IXON)
+        raw.iflag &= ~(termios.BRKINT | termios.INPCK | termios.ISTRIP | termios.IXON)
         raw.cflag &= ~(termios.CSIZE | termios.PARENB)
-        raw.cflag |= (termios.CS8)
-        raw.lflag &= ~(termios.ICANON | termios.ECHO |
-                       termios.IEXTEN | (termios.ISIG * 1))
+        raw.cflag |= termios.CS8
+        raw.lflag &= ~(
+            termios.ICANON | termios.ECHO | termios.IEXTEN | (termios.ISIG * 1)
+        )
         raw.cc[termios.VMIN] = 1
         raw.cc[termios.VTIME] = 0
         tcsetattr(self.input_fd, termios.TCSADRAIN, raw)
@@ -380,8 +414,7 @@ def prepare(self):
         self.__maybe_write_code(self._smkx)
 
         try:
-            self.old_sigwinch = signal.signal(
-                signal.SIGWINCH, self.__sigwinch)
+            self.old_sigwinch = signal.signal(signal.SIGWINCH, self.__sigwinch)
         except ValueError:
             pass
 
@@ -390,7 +423,7 @@ def restore(self):
         self.flushoutput()
         tcsetattr(self.input_fd, termios.TCSADRAIN, self.__svtermstate)
 
-        if hasattr(self, 'old_sigwinch'):
+        if hasattr(self, "old_sigwinch"):
             try:
                 signal.signal(signal.SIGWINCH, self.old_sigwinch)
                 del self.old_sigwinch
@@ -400,12 +433,12 @@ def restore(self):
 
     def __sigwinch(self, signum, frame):
         self.height, self.width = self.getheightwidth()
-        self.event_queue.insert(Event('resize', None))
+        self.event_queue.insert(Event("resize", None))
         if self.old_sigwinch != signal.SIG_DFL:
             self.old_sigwinch(signum, frame)
 
     def push_char(self, char):
-        trace('push char {char!r}', char=char)
+        trace("push char {char!r}", char=char)
         self.event_queue.push(char)
 
     def get_event(self, block=1):
@@ -451,25 +484,29 @@ def repaint_prep(self):
         if not self.__gone_tall:
             self.__posxy = 0, self.__posxy[1]
             self.__write("\r")
-            ns = len(self.screen)*['\000'*self.width]
+            ns = len(self.screen) * ["\000" * self.width]
             self.screen = ns
         else:
             self.__posxy = 0, self.__offset
             self.__move(0, self.__offset)
-            ns = self.height*['\000'*self.width]
+            ns = self.height * ["\000" * self.width]
             self.screen = ns
 
     if TIOCGWINSZ:
+
         def getheightwidth(self):
             try:
                 return int(os.environ["LINES"]), int(os.environ["COLUMNS"])
             except KeyError:
                 height, width = struct.unpack(
-                    "hhhh", ioctl(self.input_fd, TIOCGWINSZ, "\000"*8))[0:2]
+                    "hhhh", ioctl(self.input_fd, TIOCGWINSZ, "\000" * 8)
+                )[0:2]
                 if not height:
                     return 25, 80
                 return height, width
+
     else:
+
         def getheightwidth(self):
             try:
                 return int(os.environ["LINES"]), int(os.environ["COLUMNS"])
@@ -484,7 +521,7 @@ def flushoutput(self):
             if iscode:
                 self.__tputs(text)
             else:
-                os.write(self.output_fd, text.encode(self.encoding, 'replace'))
+                os.write(self.output_fd, text.encode(self.encoding, "replace"))
         del self.__buffer[:]
 
     def __tputs(self, fmt, prog=delayprog):
@@ -506,13 +543,13 @@ def __tputs(self, fmt, prog=delayprog):
             os.write(self.output_fd, fmt[:x])
             fmt = fmt[y:]
             delay = int(m.group(1))
-            if '*' in m.group(2):
+            if "*" in m.group(2):
                 delay *= self.height
             if self._pad:
-                nchars = (bps*delay)/1000
-                os.write(self.output_fd, self._pad*nchars)
+                nchars = (bps * delay) / 1000
+                os.write(self.output_fd, self._pad * nchars)
             else:
-                time.sleep(float(delay)/1000.0)
+                time.sleep(float(delay) / 1000.0)
 
     def finish(self):
         y = len(self.screen) - 1
@@ -527,25 +564,27 @@ def beep(self):
         self.flushoutput()
 
     if FIONREAD:
+
         def getpending(self):
-            e = Event('key', '', '')
+            e = Event("key", "", "")
 
             while not self.event_queue.empty():
                 e2 = self.event_queue.get()
                 e.data += e2.data
                 e.raw += e.raw
 
-            amount = struct.unpack(
-                "i", ioctl(self.input_fd, FIONREAD, "\0\0\0\0"))[0]
+            amount = struct.unpack("i", ioctl(self.input_fd, FIONREAD, "\0\0\0\0"))[0]
             data = os.read(self.input_fd, amount)
-            raw = str(data, self.encoding, 'replace')
-            #XXX: something is wrong here
+            raw = str(data, self.encoding, "replace")
+            # XXX: something is wrong here
             e.data += raw
             e.raw += raw
             return e
+
     else:
+
         def getpending(self):
-            e = Event('key', '', '')
+            e = Event("key", "", "")
 
             while not self.event_queue.empty():
                 e2 = self.event_queue.get()
@@ -554,8 +593,8 @@ def getpending(self):
 
             amount = 10000
             data = os.read(self.input_fd, amount)
-            raw = str(data, self.encoding, 'replace')
-            #XXX: something is wrong here
+            raw = str(data, self.encoding, "replace")
+            # XXX: something is wrong here
             e.data += raw
             e.raw += raw
             return e
diff --git a/pyrepl/unix_eventqueue.py b/pyrepl/unix_eventqueue.py
index 91dc076..9f3356f 100644
--- a/pyrepl/unix_eventqueue.py
+++ b/pyrepl/unix_eventqueue.py
@@ -21,14 +21,15 @@
 # Bah, this would be easier to test if curses/terminfo didn't have so
 # much non-introspectable global state.
 
+import os
 from collections import deque
+from termios import VERASE, tcgetattr
 
-from pyrepl import keymap
+from pyrepl import curses, keymap
 from pyrepl.console import Event
-from pyrepl import curses
+
 from .trace import trace
-from termios import tcgetattr, VERASE
-import os
+
 try:
     str
 except NameError:
@@ -50,8 +51,8 @@
 }
 
 
-#function keys x in 1-20 -> fX: kfX
-_keynames.update(('f%d' % i, 'kf%d' % i) for i in range(1, 21))
+# function keys x in 1-20 -> fX: kfX
+_keynames.update(("f%d" % i, "kf%d" % i) for i in range(1, 21))
 
 # this is a bit of a hack: CTRL-left and CTRL-right are not standardized
 # termios sequences: each terminal emulator implements its own slightly
@@ -65,18 +66,19 @@
 #
 CTRL_ARROW_KEYCODE = {
     # for xterm, gnome-terminal, xfce terminal, etc.
-    b'\033[1;5D': 'ctrl left',
-    b'\033[1;5C': 'ctrl right',
+    b"\033[1;5D": "ctrl left",
+    b"\033[1;5C": "ctrl right",
     # for rxvt
-    b'\033Od': 'ctrl left',
-    b'\033Oc': 'ctrl right',
+    b"\033Od": "ctrl left",
+    b"\033Oc": "ctrl right",
 }
 
+
 def general_keycodes():
     keycodes = {}
     for key, tiname in list(_keynames.items()):
         keycode = curses.tigetstr(tiname)
-        trace('key {key} tiname {tiname} keycode {keycode!r}', **locals())
+        trace("key {key} tiname {tiname} keycode {keycode!r}", **locals())
         if keycode:
             keycodes[keycode] = key
     keycodes.update(CTRL_ARROW_KEYCODE)
@@ -87,9 +89,9 @@ def EventQueue(fd, encoding):
     keycodes = general_keycodes()
     if os.isatty(fd):
         backspace = tcgetattr(fd)[6][VERASE]
-        keycodes[backspace] = str('backspace')
+        keycodes[backspace] = str("backspace")
     k = keymap.compile_keymap(keycodes)
-    trace('keymap {k!r}', k=k)
+    trace("keymap {k!r}", k=k)
     return EncodedQueue(k, encoding)
 
 
@@ -115,7 +117,7 @@ def flush_buf(self):
         return old
 
     def insert(self, event):
-        trace('added event {event}', event=event)
+        trace("added event {event}", event=event)
         self.events.append(event)
 
     def push(self, char):
@@ -124,23 +126,23 @@ def push(self, char):
         self.buf.append(ord_char)
         if char in self.k:
             if self.k is self.ck:
-                #sanity check, buffer is empty when a special key comes
+                # sanity check, buffer is empty when a special key comes
                 assert len(self.buf) == 1
             k = self.k[char]
-            trace('found map {k!r}', k=k)
+            trace("found map {k!r}", k=k)
             if isinstance(k, dict):
                 self.k = k
             else:
-                self.insert(Event('key', k, self.flush_buf()))
+                self.insert(Event("key", k, self.flush_buf()))
                 self.k = self.ck
 
         elif self.buf and self.buf[0] == 27:  # escape
             # escape sequence not recognized by our keymap: propagate it
             # outside so that i can be recognized as an M-... key (see also
             # the docstring in keymap.py, in particular the line \\E.
-            trace('unrecognized escape sequence, propagating...')
+            trace("unrecognized escape sequence, propagating...")
             self.k = self.ck
-            self.insert(Event('key', '\033', bytearray(b'\033')))
+            self.insert(Event("key", "\033", bytearray(b"\033")))
             for c in self.flush_buf()[1:]:
                 self.push(chr(c))
 
@@ -150,5 +152,5 @@ def push(self, char):
             except UnicodeError:
                 return
             else:
-                self.insert(Event('key', decoded, self.flush_buf()))
+                self.insert(Event("key", decoded, self.flush_buf()))
             self.k = self.ck
diff --git a/tests/infrastructure.py b/tests/infrastructure.py
index 2321389..2a7016e 100644
--- a/tests/infrastructure.py
+++ b/tests/infrastructure.py
@@ -18,8 +18,8 @@
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 
-from pyrepl.reader import Reader
 from pyrepl.console import Console, Event
+from pyrepl.reader import Reader
 
 
 class EqualsAnything(object):
@@ -33,7 +33,7 @@ def __eq__(self, other):
 class TestConsole(Console):
     height = 24
     width = 80
-    encoding = 'utf-8'
+    encoding = "utf-8"
 
     def __init__(self, events, verbose=False):
         self.events = events
@@ -42,8 +42,11 @@ def __init__(self, events, verbose=False):
 
     def refresh(self, screen, xy):
         if self.next_screen is not None:
-                assert screen == self.next_screen, "[ %s != %s after %r ]" % (
-                    screen, self.next_screen, self.last_event_name)
+            assert screen == self.next_screen, "[ %s != %s after %r ]" % (
+                screen,
+                self.next_screen,
+                self.last_event_name,
+            )
 
     def get_event(self, block=1):
         ev, sc = self.events.pop(0)
@@ -57,14 +60,14 @@ def get_event(self, block=1):
 
     def getpending(self):
         """Nothing pending, but do not return None here."""
-        return Event('key', '', b'')
+        return Event("key", "", b"")
 
 
 class TestReader(Reader):
     __test__ = False
 
     def get_prompt(self, lineno, cursor_on_line):
-        return ''
+        return ""
 
     def refresh(self):
         Reader.refresh(self)
diff --git a/tests/test_basic.py b/tests/test_basic.py
index 66d53ca..23df0a1 100644
--- a/tests/test_basic.py
+++ b/tests/test_basic.py
@@ -17,98 +17,142 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 import pytest
+
 from .infrastructure import read_spec
 
 
 def test_basic():
-    read_spec([(('self-insert', 'a'), ['a']),
-               ( 'accept',            ['a'])])
+    read_spec([(("self-insert", "a"), ["a"]), ("accept", ["a"])])
 
 
 def test_repeat():
-    read_spec([(('digit-arg', '3'),   ['']),
-               (('self-insert', 'a'), ['aaa']),
-               ( 'accept',            ['aaa'])])
+    read_spec(
+        [
+            (("digit-arg", "3"), [""]),
+            (("self-insert", "a"), ["aaa"]),
+            ("accept", ["aaa"]),
+        ]
+    )
 
 
 def test_kill_line():
-    read_spec([(('self-insert', 'abc'), ['abc']),
-               ( 'left',                None),
-               ( 'kill-line',           ['ab']),
-               ( 'accept',              ['ab'])])
+    read_spec(
+        [
+            (("self-insert", "abc"), ["abc"]),
+            ("left", None),
+            ("kill-line", ["ab"]),
+            ("accept", ["ab"]),
+        ]
+    )
 
 
 def test_unix_line_discard():
-    read_spec([(('self-insert', 'abc'), ['abc']),
-               ( 'left',                None),
-               ( 'unix-word-rubout',    ['c']),
-               ( 'accept',              ['c'])])
+    read_spec(
+        [
+            (("self-insert", "abc"), ["abc"]),
+            ("left", None),
+            ("unix-word-rubout", ["c"]),
+            ("accept", ["c"]),
+        ]
+    )
 
 
 def test_kill_word():
-    read_spec([(('self-insert', 'ab cd'), ['ab cd']),
-               ( 'beginning-of-line',     ['ab cd']),
-               ( 'kill-word',             [' cd']),
-               ( 'accept',                [' cd'])])
+    read_spec(
+        [
+            (("self-insert", "ab cd"), ["ab cd"]),
+            ("beginning-of-line", ["ab cd"]),
+            ("kill-word", [" cd"]),
+            ("accept", [" cd"]),
+        ]
+    )
 
 
 def test_backward_kill_word():
-    read_spec([(('self-insert', 'ab cd'), ['ab cd']),
-               ( 'backward-kill-word',    ['ab ']),
-               ( 'accept',                ['ab '])])
+    read_spec(
+        [
+            (("self-insert", "ab cd"), ["ab cd"]),
+            ("backward-kill-word", ["ab "]),
+            ("accept", ["ab "]),
+        ]
+    )
 
 
 def test_yank():
-    read_spec([(('self-insert', 'ab cd'), ['ab cd']),
-               ( 'backward-kill-word',    ['ab ']),
-               ( 'beginning-of-line',     ['ab ']),
-               ( 'yank',                  ['cdab ']),
-               ( 'accept',                ['cdab '])])
+    read_spec(
+        [
+            (("self-insert", "ab cd"), ["ab cd"]),
+            ("backward-kill-word", ["ab "]),
+            ("beginning-of-line", ["ab "]),
+            ("yank", ["cdab "]),
+            ("accept", ["cdab "]),
+        ]
+    )
 
 
 def test_yank_pop():
-    read_spec([(('self-insert', 'ab cd'), ['ab cd']),
-               ( 'backward-kill-word',    ['ab ']),
-               ( 'left',                  ['ab ']),
-               ( 'backward-kill-word',    [' ']),
-               ( 'yank',                  ['ab ']),
-               ( 'yank-pop',              ['cd ']),
-               ( 'accept',                ['cd '])])
+    read_spec(
+        [
+            (("self-insert", "ab cd"), ["ab cd"]),
+            ("backward-kill-word", ["ab "]),
+            ("left", ["ab "]),
+            ("backward-kill-word", [" "]),
+            ("yank", ["ab "]),
+            ("yank-pop", ["cd "]),
+            ("accept", ["cd "]),
+        ]
+    )
 
 
 def test_interrupt():
     with pytest.raises(KeyboardInterrupt):
-        read_spec([('interrupt', [''])])
+        read_spec([("interrupt", [""])])
 
 
 # test_suspend -- hah
 def test_up():
-    read_spec([(('self-insert', 'ab\ncd'), ['ab', 'cd']),
-               ( 'up',                     ['ab', 'cd']),
-               (('self-insert', 'e'),      ['abe', 'cd']),
-               ( 'accept',                 ['abe', 'cd'])])
+    read_spec(
+        [
+            (("self-insert", "ab\ncd"), ["ab", "cd"]),
+            ("up", ["ab", "cd"]),
+            (("self-insert", "e"), ["abe", "cd"]),
+            ("accept", ["abe", "cd"]),
+        ]
+    )
 
 
 def test_down():
-    read_spec([(('self-insert', 'ab\ncd'), ['ab', 'cd']),
-               ( 'up',                     ['ab', 'cd']),
-               (('self-insert', 'e'),      ['abe', 'cd']),
-               ( 'down',                   ['abe', 'cd']),
-               (('self-insert', 'f'),      ['abe', 'cdf']),
-               ( 'accept',                 ['abe', 'cdf'])])
+    read_spec(
+        [
+            (("self-insert", "ab\ncd"), ["ab", "cd"]),
+            ("up", ["ab", "cd"]),
+            (("self-insert", "e"), ["abe", "cd"]),
+            ("down", ["abe", "cd"]),
+            (("self-insert", "f"), ["abe", "cdf"]),
+            ("accept", ["abe", "cdf"]),
+        ]
+    )
 
 
 def test_left():
-    read_spec([(('self-insert', 'ab'), ['ab']),
-               ( 'left',               ['ab']),
-               (('self-insert', 'c'),  ['acb']),
-               ( 'accept',             ['acb'])])
+    read_spec(
+        [
+            (("self-insert", "ab"), ["ab"]),
+            ("left", ["ab"]),
+            (("self-insert", "c"), ["acb"]),
+            ("accept", ["acb"]),
+        ]
+    )
 
 
 def test_right():
-    read_spec([(('self-insert', 'ab'), ['ab']),
-               ( 'left',               ['ab']),
-               (('self-insert', 'c'),  ['acb']),
-               ( 'right',              ['acb']),
-               (('self-insert', 'd'),  ['acbd']),
-               ( 'accept',             ['acbd'])])
+    read_spec(
+        [
+            (("self-insert", "ab"), ["ab"]),
+            ("left", ["ab"]),
+            (("self-insert", "c"), ["acb"]),
+            ("right", ["acb"]),
+            (("self-insert", "d"), ["acbd"]),
+            ("accept", ["acbd"]),
+        ]
+    )
diff --git a/tests/test_bugs.py b/tests/test_bugs.py
index bc7367c..2845ff5 100644
--- a/tests/test_bugs.py
+++ b/tests/test_bugs.py
@@ -17,31 +17,31 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
+import pytest
+
 from pyrepl.historical_reader import HistoricalReader
+
 from .infrastructure import EA, TestReader, read_spec
 
 # this test case should contain as-verbatim-as-possible versions of
 # (applicable) bug reports
 
-import pytest
 
 
 class HistoricalTestReader(HistoricalReader, TestReader):
     pass
 
 
-@pytest.mark.xfail(reason='event missing', run=False)
+@pytest.mark.xfail(reason="event missing", run=False)
 def test_transpose_at_start():
-    read_spec([
-        ('transpose', [EA, '']),
-        ('accept',    [''])])
+    read_spec([("transpose", [EA, ""]), ("accept", [""])])
 
 
 def test_cmd_instantiation_crash():
     spec = [
-        ('reverse-history-isearch', ["(r-search `') "]),
-        (('key', 'left'), ['']),
-        ('accept', [''])
+        ("reverse-history-isearch", ["(r-search `') "]),
+        (("key", "left"), [""]),
+        ("accept", [""]),
     ]
     read_spec(spec, HistoricalTestReader)
 
@@ -50,6 +50,7 @@ def test_signal_failure(monkeypatch):
     import os
     import pty
     import signal
+
     from pyrepl.unix_console import UnixConsole
 
     def failing_signal(a, b):
@@ -63,9 +64,9 @@ def really_failing_signal(a, b):
         c = UnixConsole(sfd, sfd)
         c.prepare()
         c.restore()
-        monkeypatch.setattr(signal, 'signal', failing_signal)
+        monkeypatch.setattr(signal, "signal", failing_signal)
         c.prepare()
-        monkeypatch.setattr(signal, 'signal', really_failing_signal)
+        monkeypatch.setattr(signal, "signal", really_failing_signal)
         c.restore()
     finally:
         os.close(mfd)
diff --git a/tests/test_curses.py b/tests/test_curses.py
index 5188f43..472cace 100644
--- a/tests/test_curses.py
+++ b/tests/test_curses.py
@@ -1,6 +1,7 @@
 import pytest
-from pyrepl.curses import setupterm
+
 import pyrepl
+from pyrepl.curses import setupterm
 
 
 def test_setupterm(monkeypatch):
@@ -12,10 +13,10 @@ def test_setupterm(monkeypatch):
     ):
         setupterm("term_does_not_exist", 0)
 
-    monkeypatch.setenv('TERM', 'xterm')
+    monkeypatch.setenv("TERM", "xterm")
     assert setupterm(None, 0) is None
 
-    monkeypatch.delenv('TERM')
+    monkeypatch.delenv("TERM")
     with pytest.raises(
         pyrepl._minimal_curses.error,
         match=r"setupterm\(None, 0\) failed \(err=-1\)",
diff --git a/tests/test_functional.py b/tests/test_functional.py
index f557ec0..a599316 100644
--- a/tests/test_functional.py
+++ b/tests/test_functional.py
@@ -13,8 +13,7 @@
 try:
     import pexpect
 except ImportError as exc:
-    pytest.skip("could not import pexpect: {}".format(exc),
-                allow_module_level=True)
+    pytest.skip("could not import pexpect: {}".format(exc), allow_module_level=True)
 
 
 @pytest.fixture
@@ -24,18 +23,16 @@ def start_child():
     def start_child_func(env_update=None):
         assert not ret_childs, "child started already"
 
-        env = {k: v for k, v in list(os.environ.items()) if k in (
-            "TERM",
-        )}
+        env = {k: v for k, v in list(os.environ.items()) if k in ("TERM",)}
         if env_update:
             env.update(env_update)
         child = pexpect.spawn(sys.executable, timeout=5, env=env)
-        if sys.version_info >= (3, ):
+        if sys.version_info >= (3,):
             child.logfile = sys.stdout.buffer
         else:
             child.logfile = sys.stdout
         child.expect_exact(">>> ")
-        child.sendline('from pyrepl.python_reader import main')
+        child.sendline("from pyrepl.python_reader import main")
         ret_childs.append(child)
         return child
 
@@ -62,10 +59,10 @@ def child(start_child):
 
 def test_basic(child):
     child.expect_exact("->> ")
-    child.sendline('a = 40 + 2')
+    child.sendline("a = 40 + 2")
     child.expect_exact("->> ")
-    child.sendline('a')
-    child.expect_exact('42')
+    child.sendline("a")
+    child.expect_exact("42")
     child.expect_exact("->> ")
 
 
@@ -76,8 +73,9 @@ def test_sigwinch_default(child):
 
 def test_sigwinch_forwarded(start_child, tmpdir):
     with open(str(tmpdir.join("initfile")), "w") as initfile:
-        initfile.write(textwrap.dedent(
-            """
+        initfile.write(
+            textwrap.dedent(
+                """
             import signal
 
             called = []
@@ -89,7 +87,8 @@ def custom_handler(signum, frame):
 
             print("PYREPLSTARTUP called")
             """
-        ))
+            )
+        )
 
     child = start_child(env_update={"PYREPLSTARTUP": initfile.name})
     child.sendline("main()")
diff --git a/tests/test_keymap.py b/tests/test_keymap.py
index 12c4f0d..ce8df85 100644
--- a/tests/test_keymap.py
+++ b/tests/test_keymap.py
@@ -2,9 +2,11 @@
 
 
 def test_compile_keymap():
-    k = compile_keymap({
-        b'a': 'test',
-        b'bc': 'test2',
-    })
+    k = compile_keymap(
+        {
+            b"a": "test",
+            b"bc": "test2",
+        }
+    )
 
-    assert k == {b'a': 'test', b'b': {b'c': 'test2'}}
+    assert k == {b"a": "test", b"b": {b"c": "test2"}}
diff --git a/tests/test_readline.py b/tests/test_readline.py
index 9f55d1f..6cf4a38 100644
--- a/tests/test_readline.py
+++ b/tests/test_readline.py
@@ -12,46 +12,38 @@ def readline_wrapper():
     return _ReadlineWrapper(slave, slave)
 
 
-if sys.version_info < (3, ):
-    bytes_type = str
-    unicode_type = str  # noqa: F821
-else:
-    bytes_type = bytes
-    unicode_type = str
-
-
 def test_readline():
     master, slave = pty.openpty()
     readline_wrapper = _ReadlineWrapper(slave, slave)
-    os.write(master, b'input\n')
+    os.write(master, b"input\n")
 
     result = readline_wrapper.get_reader().readline()
-    assert result == b'input'
-    assert isinstance(result, bytes_type)
+    assert result == b"input"
+    assert isinstance(result, bytes)
 
 
 def test_readline_returns_unicode():
     master, slave = pty.openpty()
     readline_wrapper = _ReadlineWrapper(slave, slave)
-    os.write(master, b'input\n')
+    os.write(master, b"input\n")
 
     result = readline_wrapper.get_reader().readline(returns_unicode=True)
-    assert result == 'input'
-    assert isinstance(result, unicode_type)
+    assert result == "input"
+    assert isinstance(result, str)
 
 
 def test_raw_input():
     master, slave = pty.openpty()
     readline_wrapper = _ReadlineWrapper(slave, slave)
-    os.write(master, b'input\n')
+    os.write(master, b"input\n")
 
-    result = readline_wrapper.raw_input('prompt:')
-    if sys.version_info < (3, ):
-        assert result == b'input'
-        assert isinstance(result, bytes_type)
+    result = readline_wrapper.raw_input("prompt:")
+    if sys.version_info < (3,):
+        assert result == b"input"
+        assert isinstance(result, bytes)
     else:
-        assert result == 'input'
-        assert isinstance(result, unicode_type)
+        assert result == "input"
+        assert isinstance(result, str)
 
 
 def test_read_history_file(readline_wrapper, tmp_path):
diff --git a/tests/test_unix_reader.py b/tests/test_unix_reader.py
index 287cb0f..3c0976a 100644
--- a/tests/test_unix_reader.py
+++ b/tests/test_unix_reader.py
@@ -1,12 +1,11 @@
-
 from pyrepl.unix_eventqueue import EncodedQueue, Event
 
 
 def test_simple():
-    q = EncodedQueue({}, 'utf-8')
+    q = EncodedQueue({}, "utf-8")
 
-    a = '\u1234'
-    b = a.encode('utf-8')
+    a = "\u1234"
+    b = a.encode("utf-8")
     for c in b:
         q.push(c)
 
@@ -27,21 +26,21 @@ def send(keys):
             if event is None:
                 break
             events.append(event)
-        return events        
+        return events
 
     keymap = {
-        b'\033': {b'U': 'up', b'D': 'down'},
-        b'\xf7': 'backspace',
+        b"\033": {b"U": "up", b"D": "down"},
+        b"\xf7": "backspace",
     }
-    q = EncodedQueue(keymap, 'utf-8')
+    q = EncodedQueue(keymap, "utf-8")
 
     # normal behaviour
-    assert send(b'\033U') == [Event('key', 'up', bytearray(b'\033U'))]
-    assert send(b'\xf7') == [Event('key', 'backspace', bytearray(b'\xf7'))]
+    assert send(b"\033U") == [Event("key", "up", bytearray(b"\033U"))]
+    assert send(b"\xf7") == [Event("key", "backspace", bytearray(b"\xf7"))]
 
     # escape propagation: simulate M-backspace
-    events = send(b'\033\xf7')
+    events = send(b"\033\xf7")
     assert events == [
-        Event('key', '\033', bytearray(b'\033')),
-        Event('key', 'backspace', bytearray(b'\xf7'))
+        Event("key", "\033", bytearray(b"\033")),
+        Event("key", "backspace", bytearray(b"\xf7")),
     ]
diff --git a/tests/test_wishes.py b/tests/test_wishes.py
index d0c1e6f..d1a94a1 100644
--- a/tests/test_wishes.py
+++ b/tests/test_wishes.py
@@ -24,8 +24,11 @@
 
 
 def test_quoted_insert_repeat():
-    read_spec([
-        (('digit-arg', '3'),      ['']),
-        (('quoted-insert', None), ['']),
-        (('key', '\033'),         ['^[^[^[']),
-        (('accept', None),        None)])
+    read_spec(
+        [
+            (("digit-arg", "3"), [""]),
+            (("quoted-insert", None), [""]),
+            (("key", "\033"), ["^[^[^["]),
+            (("accept", None), None),
+        ]
+    )

From aca9b533e64a7c6a410df5dfd33b1a247e66aedf Mon Sep 17 00:00:00 2001
From: bretello <bretello@distruzione.org>
Date: Sun, 29 Oct 2023 15:15:47 +0100
Subject: [PATCH 7/9] tox: get rid of python < 3.8

---
 pyrepl/completer.py | 1 -
 tox.ini             | 2 +-
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/pyrepl/completer.py b/pyrepl/completer.py
index c768481..3c21c34 100644
--- a/pyrepl/completer.py
+++ b/pyrepl/completer.py
@@ -22,7 +22,6 @@
 
 class Completer:
     def __init__(self, ns):
-        print(f"init with {ns=}")
         self.ns = ns
 
     def complete(self, text):
diff --git a/tox.ini b/tox.ini
index cc5c970..8a74311 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = py{27,34,35,36,37,38,39,py,py3}, flake8
+envlist = py{38,39,310,311,py3}, flake8
 
 [testenv]
 deps =

From d8df5f68ea25777729a5f46e6174dc98e2b48761 Mon Sep 17 00:00:00 2001
From: bretello <bretello@distruzione.org>
Date: Sun, 29 Oct 2023 15:41:54 +0100
Subject: [PATCH 8/9] tox: use ruff as linter

---
 pyproject.toml | 16 ++++++++++++++++
 tox.ini        |  5 ++---
 2 files changed, 18 insertions(+), 3 deletions(-)
 create mode 100644 pyproject.toml

diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..43e1ec4
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,16 @@
+[tool.ruff.lint]
+select = [
+  # pycodestyle
+  "E",
+  # Pyflakes
+  "F",
+  # pyupgrade
+  "UP",
+  # flake8-bugbear
+  "B",
+  # flake8-simplify
+  "SIM",
+  # isort
+  "I",
+]
+ignore = ["F401"]
diff --git a/tox.ini b/tox.ini
index 8a74311..f5bb3c8 100644
--- a/tox.ini
+++ b/tox.ini
@@ -19,9 +19,8 @@ setenv =
 
 [testenv:qa]
 deps =
-    flake8
-    mccabe
-commands = flake8 --max-complexity=10 setup.py pyrepl tests pythoni pythoni1
+    ruff
+commands = ruff check setup.py pyrepl tests pythoni pythoni1
 
 [pytest]
 testpaths = tests

From cd3fda0616ece6e44bc86e21306c0404ff320027 Mon Sep 17 00:00:00 2001
From: bretello <bretello@distruzione.org>
Date: Sun, 29 Oct 2023 15:16:07 +0100
Subject: [PATCH 9/9] fixes or python2->python3, misc linter warnings

---
 pyrepl/_minimal_curses.py   | 10 ++---
 pyrepl/cmdrepl.py           | 16 ++++----
 pyrepl/commands.py          | 35 ++++++++--------
 pyrepl/completer.py         |  2 +-
 pyrepl/completing_reader.py | 12 +++---
 pyrepl/console.py           |  6 +--
 pyrepl/historical_reader.py | 33 ++++++---------
 pyrepl/input.py             |  2 +-
 pyrepl/keymap.py            | 12 ++----
 pyrepl/keymaps.py           |  2 -
 pyrepl/pygame_console.py    |  8 ++--
 pyrepl/pygame_keymap.py     | 12 +++---
 pyrepl/python_reader.py     | 55 +++++++++----------------
 pyrepl/reader.py            | 13 ++----
 pyrepl/readline.py          | 80 +++++++++++++------------------------
 pyrepl/simple_interact.py   |  6 +--
 pyrepl/trace.py             |  5 +--
 pyrepl/unix_console.py      | 13 +++---
 pyrepl/unix_eventqueue.py   | 10 +----
 pythoni                     | 15 ++++---
 pythoni1                    |  8 ++--
 tests/infrastructure.py     |  8 ++--
 tests/test_functional.py    |  7 +---
 tests/test_readline.py      | 15 +++----
 24 files changed, 148 insertions(+), 237 deletions(-)

diff --git a/pyrepl/_minimal_curses.py b/pyrepl/_minimal_curses.py
index c31fa3e..65621d3 100644
--- a/pyrepl/_minimal_curses.py
+++ b/pyrepl/_minimal_curses.py
@@ -45,17 +45,15 @@ def _find_clib():
 
 try:
     from __pypy__ import builtinify
-
-    builtinify  # silence broken pyflakes
 except ImportError:
-    builtinify = lambda f: f
+    def builtinify(f):
+        return f
 
 
 @builtinify
 def setupterm(termstr, fd):
-    if termstr is not None:
-        if not isinstance(termstr, bytes):
-            termstr = termstr.encode()
+    if termstr is not None and not isinstance(termstr, bytes):
+        termstr = termstr.encode()
     err = ctypes.c_int(0)
     result = clib.setupterm(termstr, fd, ctypes.byref(err))
     if result == ERR:
diff --git a/pyrepl/cmdrepl.py b/pyrepl/cmdrepl.py
index a790f07..87acaab 100644
--- a/pyrepl/cmdrepl.py
+++ b/pyrepl/cmdrepl.py
@@ -42,13 +42,13 @@
 
 class CmdReader(CR):
     def collect_keymap(self):
-        return super(CmdReader, self).collect_keymap() + (
+        return super().collect_keymap() + (
             ("\\M-\\n", "invalid-key"),
             ("\\n", "accept"),
         )
 
     def __init__(self, completions):
-        super(CmdReader, self).__init__()
+        super().__init__()
         self.completions = completions
 
     def get_completions(self, stem):
@@ -75,21 +75,21 @@ def replize(klass, history_across_invocations=1):
     #    if klass.cmdloop.im_class is not cmd.Cmd:
     #        print "this may not work"
 
-    class MultiHist(object):
+    class MultiHist:
         __history = []
 
         def __init__(self, *args, **kw):
-            super(MultiHist, self).__init__(*args, **kw)
+            super().__init__(*args, **kw)
             self.__reader = CmdReader(completions)
             self.__reader.history = self.__history
             self.__reader.historyi = len(self.__history)
 
-    class SimpleHist(object):
+    class SimpleHist:
         def __init__(self, *args, **kw):
-            super(SimpleHist, self).__init__(*args, **kw)
+            super().__init__(*args, **kw)
             self.__reader = CmdReader(completions)
 
-    class CmdLoopMixin(object):
+    class CmdLoopMixin:
         def cmdloop(self, intro=None):
             self.preloop()
             if intro is not None:
@@ -115,6 +115,6 @@ def cmdloop(self, intro=None):
     hist = MultiHist if history_across_invocations else SimpleHist
 
     class CmdRepl(hist, CmdLoopMixin, klass):
-        __name__ = "replize(%s.%s)" % (klass.__module__, klass.__name__)
+        __name__ = f"replize({klass.__module__}.{klass.__name__})"
 
     return CmdRepl
diff --git a/pyrepl/commands.py b/pyrepl/commands.py
index b4ce55f..ba7063b 100644
--- a/pyrepl/commands.py
+++ b/pyrepl/commands.py
@@ -20,7 +20,8 @@
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 import os
-import sys
+
+from pyrepl import input  # noqa: F401
 
 # Catgories of actions:
 #  killing
@@ -32,7 +33,7 @@
 # [completion]
 
 
-class Command(object):
+class Command:
     finish = 0
     kills_digit_arg = 1
 
@@ -159,21 +160,21 @@ def do(self):
 class unix_word_rubout(KillCommand):
     def do(self):
         r = self.reader
-        for i in range(r.get_arg()):
+        for _i in range(r.get_arg()):
             self.kill_range(r.bow(), r.pos)
 
 
 class kill_word(KillCommand):
     def do(self):
         r = self.reader
-        for i in range(r.get_arg()):
+        for _i in range(r.get_arg()):
             self.kill_range(r.pos, r.eow())
 
 
 class backward_kill_word(KillCommand):
     def do(self):
         r = self.reader
-        for i in range(r.get_arg()):
+        for _i in range(r.get_arg()):
             self.kill_range(r.bow(), r.pos)
 
 
@@ -232,7 +233,7 @@ def do(self):
 class up(MotionCommand):
     def do(self):
         r = self.reader
-        for i in range(r.get_arg()):
+        for _i in range(r.get_arg()):
             bol1 = r.bol()
             if bol1 == 0:
                 if r.historyi > 0:
@@ -254,7 +255,7 @@ class down(MotionCommand):
     def do(self):
         r = self.reader
         b = r.buffer
-        for i in range(r.get_arg()):
+        for _i in range(r.get_arg()):
             bol1 = r.bol()
             eol1 = r.eol()
             if eol1 == len(b):
@@ -275,7 +276,7 @@ def do(self):
 class left(MotionCommand):
     def do(self):
         r = self.reader
-        for i in range(r.get_arg()):
+        for _i in range(r.get_arg()):
             p = r.pos - 1
             if p >= 0:
                 r.pos = p
@@ -287,7 +288,7 @@ class right(MotionCommand):
     def do(self):
         r = self.reader
         b = r.buffer
-        for i in range(r.get_arg()):
+        for _i in range(r.get_arg()):
             p = r.pos + 1
             if p <= len(b):
                 r.pos = p
@@ -302,7 +303,6 @@ def do(self):
 
 class end_of_line(MotionCommand):
     def do(self):
-        r = self.reader
         self.reader.pos = self.reader.eol()
 
 
@@ -319,14 +319,14 @@ def do(self):
 class forward_word(MotionCommand):
     def do(self):
         r = self.reader
-        for i in range(r.get_arg()):
+        for _i in range(r.get_arg()):
             r.pos = r.eow()
 
 
 class backward_word(MotionCommand):
     def do(self):
         r = self.reader
-        for i in range(r.get_arg()):
+        for _i in range(r.get_arg()):
             r.pos = r.bow()
 
 
@@ -364,7 +364,7 @@ class backspace(EditCommand):
     def do(self):
         r = self.reader
         b = r.buffer
-        for i in range(r.get_arg()):
+        for _i in range(r.get_arg()):
             if r.pos > 0:
                 r.pos -= 1
                 del b[r.pos]
@@ -385,7 +385,7 @@ def do(self):
             r.update_screen()
             r.console.finish()
             raise EOFError
-        for i in range(r.get_arg()):
+        for _i in range(r.get_arg()):
             if r.pos != len(b):
                 del b[r.pos]
                 r.dirty = 1
@@ -423,15 +423,12 @@ def do(self):
 
         r = self.reader
         pending = r.console.getpending().data
-        disp = disp_str((self.event + pending))[0]
+        disp = disp_str(self.event + pending)[0]
         r.insert(disp * r.get_arg())
         r.pop_input_trans()
 
 
-from pyrepl import input
-
-
-class QITrans(object):
+class QITrans:
     def push(self, evt):
         self.evt = evt
 
diff --git a/pyrepl/completer.py b/pyrepl/completer.py
index 3c21c34..9a65d49 100644
--- a/pyrepl/completer.py
+++ b/pyrepl/completer.py
@@ -79,7 +79,7 @@ def attr_matches(self, text):
         n = len(attr)
         for word in words:
             if word[:n] == attr and word != "__builtins__":
-                matches.append("%s.%s" % (expr, word))
+                matches.append(f"{expr}.{word}")
         return matches
 
 
diff --git a/pyrepl/completing_reader.py b/pyrepl/completing_reader.py
index 34ed675..aba3808 100644
--- a/pyrepl/completing_reader.py
+++ b/pyrepl/completing_reader.py
@@ -86,7 +86,7 @@ def build_menu(cons, wordlist, start, use_brackets, sort_in_column):
     i = start
     for r in range(rows):
         row = []
-        for col in range(cols):
+        for _col in range(cols):
             row.append(item % left_align(wordlist[i], maxlen))
             i += 1
             if i >= len(wordlist):
@@ -222,10 +222,10 @@ class CompletingReader(Reader):
     sort_in_column = False
 
     def collect_keymap(self):
-        return super(CompletingReader, self).collect_keymap() + ((r"\t", "complete"),)
+        return super().collect_keymap() + ((r"\t", "complete"),)
 
     def __init__(self, console):
-        super(CompletingReader, self).__init__(console)
+        super().__init__(console)
         self.cmpltn_menu = ["[ menu 1 ]", "[ menu 2 ]"]
         self.cmpltn_menu_vis = 0
         self.cmpltn_menu_end = 0
@@ -234,12 +234,12 @@ def __init__(self, console):
             self.commands[c.__name__.replace("_", "-")] = c
 
     def after_command(self, cmd):
-        super(CompletingReader, self).after_command(cmd)
+        super().after_command(cmd)
         if not isinstance(cmd, (complete, self_insert)):
             self.cmpltn_reset()
 
     def calc_screen(self):
-        screen = super(CompletingReader, self).calc_screen()
+        screen = super().calc_screen()
         if self.cmpltn_menu_vis:
             ly = self.lxy[1]
             screen[ly:ly] = self.cmpltn_menu
@@ -248,7 +248,7 @@ def calc_screen(self):
         return screen
 
     def finish(self):
-        super(CompletingReader, self).finish()
+        super().finish()
         self.cmpltn_reset()
 
     def cmpltn_reset(self):
diff --git a/pyrepl/console.py b/pyrepl/console.py
index 95bc78d..41617f2 100644
--- a/pyrepl/console.py
+++ b/pyrepl/console.py
@@ -18,7 +18,7 @@
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 
-class Event(object):
+class Event:
     """An Event.  `evt' is 'key' or somesuch."""
 
     __slots__ = "evt", "data", "raw"
@@ -29,7 +29,7 @@ def __init__(self, evt, data, raw=""):
         self.raw = raw
 
     def __repr__(self):
-        return "Event(%r, %r)" % (self.evt, self.data)
+        return "Event({self.evt}, {self.data})"
 
     def __eq__(self, other):
         return (
@@ -37,7 +37,7 @@ def __eq__(self, other):
         )
 
 
-class Console(object):
+class Console:
     """Attributes:
 
     screen,
diff --git a/pyrepl/historical_reader.py b/pyrepl/historical_reader.py
index 1ea436c..97bae94 100644
--- a/pyrepl/historical_reader.py
+++ b/pyrepl/historical_reader.py
@@ -43,7 +43,7 @@
 )
 
 if "c" in globals():
-    del c
+    del c  # noqa: F821
 
 ISEARCH_DIRECTION_NONE = ""
 ISEARCH_DIRECTION_BACKWARDS = "r"
@@ -71,11 +71,10 @@ def do(self):
 class restore_history(commands.Command):
     def do(self):
         r = self.reader
-        if r.historyi != len(r.history):
-            if r.get_unicode() != r.history[r.historyi]:
-                r.buffer = list(r.history[r.historyi])
-                r.pos = len(r.buffer)
-                r.dirty = 1
+        if r.historyi != len(r.history) and r.get_unicode() != r.history[r.historyi]:
+            r.buffer = list(r.history[r.historyi])
+            r.pos = len(r.buffer)
+            r.dirty = 1
 
 
 class first_history(commands.Command):
@@ -111,10 +110,7 @@ def do(self):
             return
         w = words[a]
         b = r.buffer
-        if r.yank_arg_i > 0:
-            o = len(r.yank_arg_yanked)
-        else:
-            o = 0
+        o = len(r.yank_arg_yanked) if r.yank_arg_i > 0 else 0
         b[r.pos - o : r.pos] = list(w)
         r.yank_arg_yanked = w
         r.pos += len(w) - o
@@ -212,7 +208,7 @@ class HistoricalReader(R):
     """
 
     def collect_keymap(self):
-        return super(HistoricalReader, self).collect_keymap() + (
+        return super().collect_keymap() + (
             (r"\C-n", "next-history"),
             (r"\C-p", "previous-history"),
             (r"\C-o", "operate-and-get-next"),
@@ -225,7 +221,7 @@ def collect_keymap(self):
         )
 
     def __init__(self, console):
-        super(HistoricalReader, self).__init__(console)
+        super().__init__(console)
         self.history = []
         self.historyi = 0
         self.transient_history = {}
@@ -274,7 +270,7 @@ def get_item(self, i):
             return self.transient_history.get(i, self.get_unicode())
 
     def prepare(self):
-        super(HistoricalReader, self).prepare()
+        super().prepare()
         try:
             self.transient_history = {}
             if self.next_history is not None and self.next_history < len(self.history):
@@ -292,9 +288,9 @@ def prepare(self):
     def get_prompt(self, lineno, cursor_on_line):
         if cursor_on_line and self.isearch_direction != ISEARCH_DIRECTION_NONE:
             d = "rf"[self.isearch_direction == ISEARCH_DIRECTION_FORWARDS]
-            return "(%s-search `%s') " % (d, self.isearch_term)
+            return f"({d}-search `{self.isearch_term}') "
         else:
-            return super(HistoricalReader, self).get_prompt(lineno, cursor_on_line)
+            return super().get_prompt(lineno, cursor_on_line)
 
     def isearch_next(self):
         st = self.isearch_term
@@ -303,10 +299,7 @@ def isearch_next(self):
         s = self.get_unicode()
         forwards = self.isearch_direction == ISEARCH_DIRECTION_FORWARDS
         while 1:
-            if forwards:
-                p = s.find(st, p + 1)
-            else:
-                p = s.rfind(st, 0, p + len(st) - 1)
+            p = s.find(st, p + 1) if forwards else s.rfind(st, 0, p + len(st) - 1)
             if p != -1:
                 self.select_item(i)
                 self.pos = p
@@ -325,7 +318,7 @@ def isearch_next(self):
                     p = len(s)
 
     def finish(self):
-        super(HistoricalReader, self).finish()
+        super().finish()
         ret = self.get_unicode()
         for i, t in list(self.transient_history.items()):
             if i < len(self.history) and i != self.historyi:
diff --git a/pyrepl/input.py b/pyrepl/input.py
index 5180c5f..16de45b 100644
--- a/pyrepl/input.py
+++ b/pyrepl/input.py
@@ -40,7 +40,7 @@
 from .trace import trace
 
 
-class InputTranslator(object):
+class InputTranslator:
     def push(self, evt):
         pass
 
diff --git a/pyrepl/keymap.py b/pyrepl/keymap.py
index e13c929..85f701e 100644
--- a/pyrepl/keymap.py
+++ b/pyrepl/keymap.py
@@ -182,10 +182,7 @@ def _parse_key1(key, s):
         if len(ret) > 1:
             raise KeySpecError("\\C- must be followed by a character")
         ret = chr(ord(ret) & 0x1F)  # curses.ascii.ctrl()
-    if meta:
-        ret = ["\033", ret]
-    else:
-        ret = [ret]
+    ret = ["\x1b", ret] if meta else [ret]
     return ret, s
 
 
@@ -201,16 +198,13 @@ def parse_keys(key):
 def compile_keymap(keymap, empty=b""):
     r = {}
     for key, value in list(keymap.items()):
-        if isinstance(key, bytes):
-            first = key[:1]
-        else:
-            first = key[0]
+        first = key[:1] if isinstance(key, bytes) else key[0]
         r.setdefault(first, {})[key[1:]] = value
     for key, value in list(r.items()):
         if empty in value:
             if len(value) != 1:
                 raise KeySpecError(
-                    "key definitions for %s clash" % (list(value.values()),)
+                    f"key definitions for {list(value.values())} clash"
                 )
             else:
                 r[key] = value[empty]
diff --git a/pyrepl/keymaps.py b/pyrepl/keymaps.py
index e9294e0..98bdf00 100644
--- a/pyrepl/keymaps.py
+++ b/pyrepl/keymaps.py
@@ -134,5 +134,3 @@
     "vi-insert": reader_vi_insert_keymap,
     "vi-command": reader_vi_command_keymap,
 }
-
-del c  # from the listcomps
diff --git a/pyrepl/pygame_console.py b/pyrepl/pygame_console.py
index 50febbb..846c340 100644
--- a/pyrepl/pygame_console.py
+++ b/pyrepl/pygame_console.py
@@ -25,7 +25,7 @@
 # during command execution and zap the executor process.  Making this
 # work on non-Unix is expected to be even more entertaining.
 
-import types
+# ruff: noqa: F405, F403
 
 import pygame
 from pygame.locals import *
@@ -226,7 +226,7 @@ def getheightwidth(self):
         )
 
     def tr_event(self, pyg_event):
-        shift = bool(pyg_event.mod & KMOD_SHIFT)
+        bool(pyg_event.mod & KMOD_SHIFT)
         ctrl = bool(pyg_event.mod & KMOD_CTRL)
         meta = bool(pyg_event.mod & (KMOD_ALT | KMOD_META))
 
@@ -326,8 +326,8 @@ def repaint(self):
         # perhaps we should consolidate grobs?
         self.pygame_screen.fill(colors.bg)
         self.paint_margin()
-        for (y, x), surf, text in self.grobs:
-            if surf and 0 < y + self.scroll:
+        for (y, x), surf, _text in self.grobs:
+            if surf and y + self.scroll > 0:
                 self.pygame_screen.blit(surf, (lmargin + x, tmargin + y + self.scroll))
         pygame.display.update()
 
diff --git a/pyrepl/pygame_keymap.py b/pyrepl/pygame_keymap.py
index f74d1bd..8d1d049 100644
--- a/pyrepl/pygame_keymap.py
+++ b/pyrepl/pygame_keymap.py
@@ -36,6 +36,8 @@
 # XXX it's actually possible to test this module, so it should have a
 # XXX test suite.
 
+# ruff: noqa: F405, F403
+
 from pygame.locals import *
 
 _escapes = {
@@ -186,7 +188,7 @@ def _compile_keymap(keymap):
         if () in value:
             if len(value) != 1:
                 raise KeySpecError(
-                    "key definitions for %s clash" % (list(value.values()),)
+                    f"key definitions for {list(value.values())} clash"
                 )
             else:
                 r[key] = value[()]
@@ -242,11 +244,11 @@ def unparse_key(keyseq):
         elif c in _unescapes:
             p = _unescapes[c]
         elif ord(c) < ord(" "):
-            p = "\\C-%s" % (chr(ord(c) + 96),)
+            p = f"\\C-{chr(ord(c) + 96)}"
         elif ord(" ") <= ord(c) <= ord("~"):
             p = c
         else:
-            p = "\\%03o" % (ord(c),)
+            p = f"\\{ord(c):03o}"
         return p + unparse_key(r)
 
 
@@ -268,11 +270,11 @@ def _unparse_keyf(keyseq):
         elif c in _unescapes:
             p = _unescapes[c]
         elif ord(c) < ord(" "):
-            p = "C-%s" % (chr(ord(c) + 96),)
+            p = f"C-{chr(ord(c) + 96)}"
         elif ord(" ") <= ord(c) <= ord("~"):
             p = c
         else:
-            p = "\\%03o" % (ord(c),)
+            p = f"\\{ord(c):03o}"
         return [p] + _unparse_keyf(r)
 
 
diff --git a/pyrepl/python_reader.py b/pyrepl/python_reader.py
index 73285db..8072313 100644
--- a/pyrepl/python_reader.py
+++ b/pyrepl/python_reader.py
@@ -31,16 +31,10 @@
 import traceback
 import warnings
 
-from pyrepl import (commands, completer, completing_reader, module_lister,
-                    reader)
+from pyrepl import commands, completer, completing_reader, module_lister, reader
 from pyrepl.completing_reader import CompletingReader
 from pyrepl.historical_reader import HistoricalReader
 
-try:
-    str
-except:
-    str = str
-
 try:
     imp.find_module("twisted")
 except ImportError:
@@ -56,21 +50,10 @@ def eat_it(*args):
     pass
 
 
-if sys.version_info >= (3, 0):
-
-    def _reraise(cls, val, tb):
-        __tracebackhide__ = True
-        assert hasattr(val, "__traceback__")
-        raise val
-
-else:
-    exec(
-        """
 def _reraise(cls, val, tb):
     __tracebackhide__ = True
-    raise cls, val, tb
-"""
-    )
+    assert hasattr(val, "__traceback__")
+    raise val
 
 
 class maybe_accept(commands.Command):
@@ -101,20 +84,20 @@ def saver(reader=reader):
             fp.write(
                 b"\n".join(item.encode("unicode_escape") for item in reader.history)
             )
-    except IOError as e:
+    except OSError as e:
         print(e)
         pass
 
 
 class PythonicReader(CompletingReader, HistoricalReader):
     def collect_keymap(self):
-        return super(PythonicReader, self).collect_keymap() + (
+        return super().collect_keymap() + (
             (r"\n", "maybe-accept"),
             (r"\M-\n", "insert-nl"),
         )
 
     def __init__(self, console, locals, compiler=None):
-        super(PythonicReader, self).__init__(console)
+        super().__init__(console)
         self.completer = completer.Completer(locals)
         st = self.syntax_table
         for c in "._0123456789":
@@ -124,18 +107,17 @@ def __init__(self, console, locals, compiler=None):
             self.compiler = CommandCompiler()
         else:
             self.compiler = compiler
+
         try:
-            file = open(os.path.expanduser("~/.pythoni.hist"), "rb")
-        except IOError:
+            with open(os.path.expanduser("~/.pythoni.hist"), "rb") as fh:
+                lines = fh.readlines()
+            self.history = [
+                line.rstrip(b"\n").decode("unicode_escape") for line in lines
+            ]
+        except FileNotFoundError:
             self.history = []
-        else:
-            try:
-                lines = file.readlines()
-                self.history = [x.rstrip(b"\n").decode("unicode_escape") for x in lines]
-            except:
-                self.history = []
-            self.historyi = len(self.history)
-            file.close()
+        self.historyi = len(self.history)
+
         atexit.register(lambda: saver(self))
         for c in [maybe_accept]:
             self.commands[c.__name__] = c
@@ -194,7 +176,7 @@ def run_user_init_file(self):
         else:
             return
         try:
-            with open(initfile, "r") as f:
+            with open(initfile) as f:
                 exec(compile(f.read(), initfile, "exec"), self.locals, self.locals)
         except:
             etype, value, tb = sys.exc_info()
@@ -381,9 +363,8 @@ def main(
 ):
     si, se, so = sys.stdin, sys.stderr, sys.stdout
     try:
-        if 0 and use_pygame_console:  # pygame currently borked
-            from pyrepl.pygame_console import (FakeStdin, FakeStdout,
-                                               PyGameConsole)
+        if False:  # pygame currently borked
+            from pyrepl.pygame_console import FakeStdin, FakeStdout, PyGameConsole
 
             con = PyGameConsole()
             sys.stderr = sys.stdout = FakeStdout(con)
diff --git a/pyrepl/reader.py b/pyrepl/reader.py
index c79ba66..3812278 100644
--- a/pyrepl/reader.py
+++ b/pyrepl/reader.py
@@ -168,11 +168,7 @@ def make_default_syntax_table():
     ]
 )
 
-if "c" in globals():  # only on python 2.x
-    del c  # from the listcomps
-
-
-class Reader(object):
+class Reader:
     """The Reader class implements the bare bones of a command reader,
     handling such details as editing and cursor motion.  What it does
     not support are such things as completion or history support -
@@ -576,10 +572,7 @@ def handle1(self, block=1):
             else:
                 translate = False
 
-            if translate:
-                cmd = self.input_trans.get()
-            else:
-                cmd = event.evt, event.data
+            cmd = self.input_trans.get() if translate else (event.evt, event.data)
 
             if cmd is None:
                 if block:
@@ -624,7 +617,7 @@ def get_buffer(self, encoding=None):
 
     def get_unicode(self):
         """Return the current buffer as a unicode string."""
-        return str("").join(self.buffer)
+        return "".join(self.buffer)
 
 
 def test():
diff --git a/pyrepl/readline.py b/pyrepl/readline.py
index a4869df..23ba864 100644
--- a/pyrepl/readline.py
+++ b/pyrepl/readline.py
@@ -26,6 +26,7 @@
 extensions for multiline input.
 """
 
+import contextlib
 import os
 import sys
 
@@ -67,7 +68,7 @@
 # ____________________________________________________________
 
 
-class ReadlineConfig(object):
+class ReadlineConfig:
     readline_completer = None
     completer_delims = dict.fromkeys(" \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>/?")
 
@@ -135,17 +136,15 @@ def get_trimmed_history(self, maxlength):
     more_lines = None
 
     def collect_keymap(self):
-        return super(ReadlineAlikeReader, self).collect_keymap() + (
-            (r"\n", "maybe-accept"),
-        )
+        return super().collect_keymap() + ((r"\n", "maybe-accept"),)
 
     def __init__(self, console):
-        super(ReadlineAlikeReader, self).__init__(console)
+        super().__init__(console)
         self.commands["maybe_accept"] = maybe_accept
         self.commands["maybe-accept"] = maybe_accept
 
     def after_command(self, cmd):
-        super(ReadlineAlikeReader, self).after_command(cmd)
+        super().after_command(cmd)
         if self.more_lines is None:
             # Force single-line input if we are in raw_input() mode.
             # Although there is no direct way to add a \n in this mode,
@@ -177,7 +176,7 @@ def do(self):
             self.finish = 1
 
 
-class _ReadlineWrapper(object):
+class _ReadlineWrapper:
     reader = None
     saved_history_length = -1
     startup_hook = None
@@ -202,7 +201,7 @@ def get_reader(self):
             self.reader.config = self.config
         return self.reader
 
-    def raw_input(self, prompt=""):
+    def raw_input(self, prompt="") -> str:
         try:
             reader = self.get_reader()
         except _error:
@@ -220,11 +219,7 @@ def raw_input(self, prompt=""):
             self.stderr.flush()
 
         ret = reader.readline(startup_hook=self.startup_hook)
-        if not PY3:
-            return ret
 
-        # Unicode/str is required for Python 3 (3.5.2).
-        # Ref: https://bitbucket.org/pypy/pyrepl/issues/20/#comment-30647029
         return str(ret, ENCODING)
 
     def multiline_input(self, more_lines, ps1, ps2, returns_unicode=False):
@@ -259,15 +254,8 @@ def get_completer_delims(self):
         chars.sort()
         return "".join(chars)
 
-    def _histline(self, line):
-        line = line.rstrip("\n")
-        if PY3:
-            return line
-
-        try:
-            return str(line, ENCODING)
-        except UnicodeDecodeError:  # bah, silently fall back...
-            return str(line, "utf-8", "replace")
+    def _histline(self, line: str):
+        return line.rstrip("\n")
 
     def get_history_length(self):
         return self.saved_history_length
@@ -284,39 +272,30 @@ def read_history_file(self, filename="~/.history"):
         # history item: we use \r\n instead of just \n.  If the history
         # file is passed to GNU readline, the extra \r are just ignored.
         history = self.get_reader().history
-        f = open(os.path.expanduser(filename), "r")
-        buffer = []
-        for line in f:
-            if line.endswith("\r\n"):
-                buffer.append(line)
-            else:
-                line = self._histline(line)
-                if buffer:
-                    line = "".join(buffer).replace("\r", "") + line
-                    del buffer[:]
-                if line:
-                    history.append(line)
-        f.close()
+        with open(os.path.expanduser(filename)) as f:
+            buffer = []
+            for line in f:
+                if line.endswith("\r\n"):
+                    buffer.append(line)
+                else:
+                    line = self._histline(line)
+                    if buffer:
+                        line = "".join(buffer).replace("\r", "") + line
+                        del buffer[:]
+                    if line:
+                        history.append(line)
 
     def write_history_file(self, filename="~/.history"):
         maxlength = self.saved_history_length
         history = self.get_reader().get_trimmed_history(maxlength)
         entries = ""
         for entry in history:
-            # if we are on py3k, we don't need to encode strings before
-            # writing it to a file
-            if isinstance(entry, str) and sys.version_info < (3,):
-                entry = entry.encode("utf-8")
             entry = entry.replace("\n", "\r\n")  # multiline history support
             entries += entry + "\n"
 
         fname = os.path.expanduser(filename)
-        if PY3:
-            f = open(fname, "w", encoding="utf-8")
-        else:
-            f = open(fname, "w")
-        f.write(entries)
-        f.close()
+        with open(fname, "w", encoding="utf-8") as f:
+            f.write(entries)
 
     def clear_history(self):
         del self.get_reader().history[:]
@@ -325,8 +304,8 @@ def get_history_item(self, index):
         history = self.get_reader().history
         if 1 <= index <= len(history):
             return history[index - 1]
-        else:
-            return None  # blame readline.c for not raising
+
+        return None  # blame readline.c for not raising
 
     def remove_history_item(self, index):
         history = self.get_reader().history
@@ -351,9 +330,7 @@ def set_startup_hook(self, function=None):
         self.startup_hook = function
 
     def get_line_buffer(self):
-        if PY3:
-            return self.get_reader().get_unicode()
-        return self.get_reader().get_buffer()
+        return self.get_reader().get_unicode()
 
     def _get_idxs(self):
         start = cursor = self.get_reader().pos
@@ -430,6 +407,7 @@ def stub(*args, **kwds):
 
 
 def _setup():
+    # TODO: is the raw_input logic still required?
     global _old_raw_input
     if _old_raw_input is not None:
         return
@@ -455,10 +433,8 @@ def _old_raw_input(prompt=""):
             # should not really fail in _wrapper.raw_input().  If it still
             # does, then we will just cancel the redirection and call again
             # the built-in raw_input().
-            try:
+            with contextlib.suppress(AttributeError):
                 del sys.__raw_input__
-            except AttributeError:
-                pass
             return input(prompt)
 
         sys.__raw_input__ = _wrapper.raw_input
diff --git a/pyrepl/simple_interact.py b/pyrepl/simple_interact.py
index bb3e1ce..068c411 100644
--- a/pyrepl/simple_interact.py
+++ b/pyrepl/simple_interact.py
@@ -47,11 +47,7 @@ def run_multiline_interactive_console(mainmodule=None, future_flags=0):
         console.compile.compiler.flags |= future_flags
 
     def more_lines(unicodetext):
-        if sys.version_info < (3,):
-            # ooh, look at the hack:
-            src = "#coding:utf-8\n" + unicodetext.encode("utf-8")
-        else:
-            src = unicodetext
+        src = unicodetext
         try:
             code = console.compile(src, "<stdin>", "single")
         except (OverflowError, SyntaxError, ValueError):
diff --git a/pyrepl/trace.py b/pyrepl/trace.py
index f141660..5c95724 100644
--- a/pyrepl/trace.py
+++ b/pyrepl/trace.py
@@ -2,10 +2,7 @@
 
 trace_filename = os.environ.get("PYREPL_TRACE")
 
-if trace_filename is not None:
-    trace_file = open(trace_filename, "a")
-else:
-    trace_file = None
+trace_file = open(trace_filename, "a") if trace_filename is not None else None
 
 
 def trace(line, *k, **kw):
diff --git a/pyrepl/unix_console.py b/pyrepl/unix_console.py
index b7fb3e6..b87a6c5 100644
--- a/pyrepl/unix_console.py
+++ b/pyrepl/unix_console.py
@@ -19,6 +19,7 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
+import contextlib
 import errno
 import os
 import re
@@ -157,7 +158,7 @@ def __init__(self, f_in=0, f_out=1, term=None, encoding=None):
 
         ## work out how we're going to sling the cursor around
         # hpa don't work in windows telnet :-(
-        if 0 and self._hpa:
+        if False:
             self.__move_x = self.__move_x_hpa
         elif self._cub and self._cuf:
             self.__move_x = self.__move_x_cub_cuf
@@ -232,7 +233,7 @@ def refresh(self, screen, c_xy):
             self.__hide_cursor()
             self.__write_code(self._cup, 0, 0)
             self.__posxy = 0, old_offset
-            for i in range(old_offset - offset):
+            for _ in range(old_offset - offset):
                 self.__write_code(self._ri)
                 oldscr.pop(-1)
                 oldscr.insert(0, "")
@@ -240,7 +241,7 @@ def refresh(self, screen, c_xy):
             self.__hide_cursor()
             self.__write_code(self._cup, self.height - 1, 0)
             self.__posxy = 0, old_offset + self.height - 1
-            for i in range(offset - old_offset):
+            for _ in range(offset - old_offset):
                 self.__write_code(self._ind)
                 oldscr.pop(0)
                 oldscr.append("")
@@ -413,10 +414,8 @@ def prepare(self):
 
         self.__maybe_write_code(self._smkx)
 
-        try:
+        with contextlib.suppress(ValueError):
             self.old_sigwinch = signal.signal(signal.SIGWINCH, self.__sigwinch)
-        except ValueError:
-            pass
 
     def restore(self):
         self.__maybe_write_code(self._rmkx)
@@ -447,7 +446,7 @@ def get_event(self, block=1):
                 # All hail Unix!
                 try:
                     self.push_char(os.read(self.input_fd, 1))
-                except (IOError, OSError) as err:
+                except OSError as err:
                     if err.errno == errno.EINTR:
                         if not self.event_queue.empty():
                             return self.event_queue.get()
diff --git a/pyrepl/unix_eventqueue.py b/pyrepl/unix_eventqueue.py
index 9f3356f..39e81b8 100644
--- a/pyrepl/unix_eventqueue.py
+++ b/pyrepl/unix_eventqueue.py
@@ -30,12 +30,6 @@
 
 from .trace import trace
 
-try:
-    str
-except NameError:
-    str = str
-
-
 _keynames = {
     "delete": "kdch1",
     "down": "kcud1",
@@ -89,13 +83,13 @@ def EventQueue(fd, encoding):
     keycodes = general_keycodes()
     if os.isatty(fd):
         backspace = tcgetattr(fd)[6][VERASE]
-        keycodes[backspace] = str("backspace")
+        keycodes[backspace] = "backspace"
     k = keymap.compile_keymap(keycodes)
     trace("keymap {k!r}", k=k)
     return EncodedQueue(k, encoding)
 
 
-class EncodedQueue(object):
+class EncodedQueue:
     def __init__(self, keymap, encoding):
         self.k = self.ck = keymap
         self.events = deque()
diff --git a/pythoni b/pythoni
index 4c11eba..2d95ea1 100755
--- a/pythoni
+++ b/pythoni
@@ -19,18 +19,21 @@
 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-import locale, pdb, sys
+import locale
+import pdb
+import sys
+
 # I forget exactly why this is necessary:
 try:
-    locale.setlocale(locale.LC_ALL, '')
+    locale.setlocale(locale.LC_ALL, "")
 except locale.Error:
-    pass # oh well
+    pass  # oh well
 
 
-from pyrepl.python_reader import main
 from pyrepl import cmdrepl
+from pyrepl.python_reader import main
 
 # whizzy feature:  graft pyrepl support onto pdb
-#pdb.Pdb = cmdrepl.replize(pdb.Pdb, 1)
+# pdb.Pdb = cmdrepl.replize(pdb.Pdb, 1)
 
-main(use_pygame_console=('pg' in sys.argv))
+main(use_pygame_console=("pg" in sys.argv))
diff --git a/pythoni1 b/pythoni1
index ee5f289..4dde86e 100755
--- a/pythoni1
+++ b/pythoni1
@@ -10,10 +10,10 @@ import sys
 from pyrepl import readline
 from pyrepl.simple_interact import run_multiline_interactive_console
 
-sys.modules['readline'] = readline
+sys.modules["readline"] = readline
 
-if os.getenv('PYTHONSTARTUP'):
-    exec(open(os.getenv('PYTHONSTARTUP')).read())
+if os.getenv("PYTHONSTARTUP"):
+    exec(open(os.getenv("PYTHONSTARTUP")).read())
 
-print('Python', sys.version)
+print("Python", sys.version)
 run_multiline_interactive_console()
diff --git a/tests/infrastructure.py b/tests/infrastructure.py
index 2a7016e..7557538 100644
--- a/tests/infrastructure.py
+++ b/tests/infrastructure.py
@@ -22,7 +22,7 @@
 from pyrepl.reader import Reader
 
 
-class EqualsAnything(object):
+class EqualsAnything:
     def __eq__(self, other):
         return True
 
@@ -42,10 +42,8 @@ def __init__(self, events, verbose=False):
 
     def refresh(self, screen, xy):
         if self.next_screen is not None:
-            assert screen == self.next_screen, "[ %s != %s after %r ]" % (
-                screen,
-                self.next_screen,
-                self.last_event_name,
+            assert screen == self.next_screen, (
+                f"[ {screen} != {self.next_screen}" "after {self.last_event_name} ]"
             )
 
     def get_event(self, block=1):
diff --git a/tests/test_functional.py b/tests/test_functional.py
index a599316..bb01ea9 100644
--- a/tests/test_functional.py
+++ b/tests/test_functional.py
@@ -13,7 +13,7 @@
 try:
     import pexpect
 except ImportError as exc:
-    pytest.skip("could not import pexpect: {}".format(exc), allow_module_level=True)
+    pytest.skip(f"could not import pexpect: {exc}", allow_module_level=True)
 
 
 @pytest.fixture
@@ -27,10 +27,7 @@ def start_child_func(env_update=None):
         if env_update:
             env.update(env_update)
         child = pexpect.spawn(sys.executable, timeout=5, env=env)
-        if sys.version_info >= (3,):
-            child.logfile = sys.stdout.buffer
-        else:
-            child.logfile = sys.stdout
+        child.logfile = sys.stdout.buffer
         child.expect_exact(">>> ")
         child.sendline("from pyrepl.python_reader import main")
         ret_childs.append(child)
diff --git a/tests/test_readline.py b/tests/test_readline.py
index 6cf4a38..16b8411 100644
--- a/tests/test_readline.py
+++ b/tests/test_readline.py
@@ -1,6 +1,5 @@
 import os
 import pty
-import sys
 
 import pytest
 from pyrepl.readline import _ReadlineWrapper
@@ -38,12 +37,8 @@ def test_raw_input():
     os.write(master, b"input\n")
 
     result = readline_wrapper.raw_input("prompt:")
-    if sys.version_info < (3,):
-        assert result == b"input"
-        assert isinstance(result, bytes)
-    else:
-        assert result == "input"
-        assert isinstance(result, str)
+    assert result == "input"
+    assert isinstance(result, str)
 
 
 def test_read_history_file(readline_wrapper, tmp_path):
@@ -70,7 +65,7 @@ def test_write_history_file(readline_wrapper, tmp_path):
 
     readline_wrapper.write_history_file(str(histfile))
 
-    with open(str(histfile), "r") as f:
+    with open(str(histfile)) as f:
         assert f.readlines() == ["foo\n", "bar\n"]
 
 
@@ -84,7 +79,7 @@ def test_write_history_file_with_exception(readline_wrapper, tmp_path):
     class BadEntryException(Exception):
         pass
 
-    class BadEntry(object):
+    class BadEntry:
         @classmethod
         def replace(cls, *args):
             raise BadEntryException
@@ -95,5 +90,5 @@ def replace(cls, *args):
     with pytest.raises(BadEntryException):
         readline_wrapper.write_history_file(str(histfile))
 
-    with open(str(histfile), "r") as f:
+    with open(str(histfile)) as f:
         assert f.readlines() == ["foo\n", "bar\n"]