From e049c81f41a56f842c6f51a82554281df98b5753 Mon Sep 17 00:00:00 2001
From: Michael Webster <miketwebster@gmail.com>
Date: Mon, 11 Jul 2022 14:48:03 -0400
Subject: [PATCH] Revert "mako: update to 1.2.0 from 1.1.4"

This reverts commit 4afe864d7d5c77c88de3dc5c1569a3f3446e178b.
---
 third_party/mako/README.chromium              |  11 +-
 third_party/mako/mako/LICENSE                 |   2 +-
 third_party/mako/mako/MANIFEST.in             |   2 +-
 third_party/mako/mako/Mako.egg-info/PKG-INFO  |  80 +++
 .../mako/mako/Mako.egg-info/SOURCES.txt       | 161 +++++
 .../mako/Mako.egg-info/dependency_links.txt   |   1 +
 .../mako/mako/Mako.egg-info/entry_points.txt  |  20 +
 .../mako/mako/Mako.egg-info/not-zip-safe      |   1 +
 .../mako/mako/Mako.egg-info/requires.txt      |   7 +
 .../mako/mako/Mako.egg-info/top_level.txt     |   1 +
 third_party/mako/mako/PKG-INFO                |  80 +++
 third_party/mako/mako/mako/__init__.py        |   4 +-
 third_party/mako/mako/mako/_ast_util.py       |  13 +-
 third_party/mako/mako/mako/ast.py             |  29 +-
 third_party/mako/mako/mako/cache.py           |   9 +-
 third_party/mako/mako/mako/cmd.py             |   9 +-
 third_party/mako/mako/mako/codegen.py         |  58 +-
 third_party/mako/mako/mako/compat.py          | 141 ++++-
 third_party/mako/mako/mako/exceptions.py      |  37 +-
 third_party/mako/mako/mako/ext/autohandler.py |   2 +-
 third_party/mako/mako/mako/ext/babelplugin.py |  11 +-
 .../mako/mako/mako/ext/beaker_cache.py        |   4 +-
 third_party/mako/mako/mako/ext/extract.py     |  22 +-
 .../mako/mako/mako/ext/linguaplugin.py        |  36 +-
 .../mako/mako/mako/ext/preprocessors.py       |   2 +-
 .../mako/mako/mako/ext/pygmentplugin.py       |  19 +-
 third_party/mako/mako/mako/ext/turbogears.py  |   6 +-
 third_party/mako/mako/mako/filters.py         |  96 ++-
 third_party/mako/mako/mako/lexer.py           | 230 +++----
 third_party/mako/mako/mako/lookup.py          |  58 +-
 third_party/mako/mako/mako/parsetree.py       | 117 ++--
 third_party/mako/mako/mako/pygen.py           |  42 +-
 third_party/mako/mako/mako/pyparser.py        |  82 ++-
 third_party/mako/mako/mako/runtime.py         |  90 +--
 third_party/mako/mako/mako/template.py        | 119 +++-
 .../mako/mako/mako/testing/__init__.py        |   0
 third_party/mako/mako/mako/testing/_config.py | 128 ----
 .../mako/mako/mako/testing/assertions.py      | 167 -----
 third_party/mako/mako/mako/testing/config.py  |  17 -
 .../mako/mako/mako/testing/exclusions.py      |  80 ---
 .../mako/mako/mako/testing/fixtures.py        | 109 ----
 third_party/mako/mako/mako/testing/helpers.py |  67 --
 third_party/mako/mako/mako/util.py            |  60 +-
 third_party/mako/mako/pyproject.toml          |   7 -
 third_party/mako/mako/setup.cfg               |  74 +--
 third_party/mako/mako/setup.py                |  98 ++-
 third_party/mako/mako/test/__init__.py        | 211 +++++++
 .../mako/mako/test/ext/test_babelplugin.py    | 156 ++---
 .../mako/mako/test/ext/test_linguaplugin.py   |  42 +-
 .../mako/mako/test/module_to_import.py        |   2 -
 .../mako/mako/test/templates/chs_unicode.html |  11 +
 .../mako/test/templates/foo/modtest.html.py   |  20 +-
 .../mako/test/templates/read_unicode.html     |  10 +
 .../mako/mako/test/templates/runtimeerr.html  |   4 +
 .../test/templates/subdir/foo/modtest.html.py |  22 +-
 .../test/templates/unicode_arguments.html     |   9 +
 .../mako/test/templates/unicode_code.html     |   7 +
 .../mako/test/templates/unicode_expr.html     |   2 +
 third_party/mako/mako/test/test_ast.py        | 112 ++--
 third_party/mako/mako/test/test_block.py      |   6 +-
 third_party/mako/mako/test/test_cache.py      |  60 +-
 third_party/mako/mako/test/test_call.py       |   8 +-
 third_party/mako/mako/test/test_cmd.py        |  35 +-
 third_party/mako/mako/test/test_decorators.py |   6 +-
 third_party/mako/mako/test/test_def.py        |  21 +-
 third_party/mako/mako/test/test_exceptions.py | 205 +++++--
 third_party/mako/mako/test/test_filters.py    |  71 ++-
 .../mako/mako/test/test_inheritance.py        |  30 +-
 third_party/mako/mako/test/test_lexer.py      | 220 ++++---
 third_party/mako/mako/test/test_lookup.py     |  72 +--
 third_party/mako/mako/test/test_loop.py       |  15 +-
 third_party/mako/mako/test/test_lru.py        |   4 +-
 third_party/mako/mako/test/test_namespace.py  |  45 +-
 third_party/mako/mako/test/test_pygen.py      |   9 +-
 third_party/mako/mako/test/test_runtime.py    |   6 +-
 third_party/mako/mako/test/test_template.py   | 573 ++++++++++++------
 third_party/mako/mako/test/test_tgplugin.py   |  17 +-
 third_party/mako/mako/test/test_util.py       |  36 +-
 third_party/mako/mako/test/testing/dummy.cfg  |  25 -
 .../mako/mako/test/testing/test_config.py     | 176 ------
 third_party/mako/mako/test/util.py            |  13 +
 third_party/mako/mako/tox.ini                 |   8 +-
 82 files changed, 2660 insertions(+), 2018 deletions(-)
 create mode 100644 third_party/mako/mako/Mako.egg-info/PKG-INFO
 create mode 100644 third_party/mako/mako/Mako.egg-info/SOURCES.txt
 create mode 100644 third_party/mako/mako/Mako.egg-info/dependency_links.txt
 create mode 100644 third_party/mako/mako/Mako.egg-info/entry_points.txt
 create mode 100644 third_party/mako/mako/Mako.egg-info/not-zip-safe
 create mode 100644 third_party/mako/mako/Mako.egg-info/requires.txt
 create mode 100644 third_party/mako/mako/Mako.egg-info/top_level.txt
 create mode 100644 third_party/mako/mako/PKG-INFO
 delete mode 100644 third_party/mako/mako/mako/testing/__init__.py
 delete mode 100644 third_party/mako/mako/mako/testing/_config.py
 delete mode 100644 third_party/mako/mako/mako/testing/assertions.py
 delete mode 100644 third_party/mako/mako/mako/testing/config.py
 delete mode 100644 third_party/mako/mako/mako/testing/exclusions.py
 delete mode 100644 third_party/mako/mako/mako/testing/fixtures.py
 delete mode 100644 third_party/mako/mako/mako/testing/helpers.py
 delete mode 100644 third_party/mako/mako/pyproject.toml
 delete mode 100644 third_party/mako/mako/test/module_to_import.py
 create mode 100644 third_party/mako/mako/test/templates/chs_unicode.html
 create mode 100644 third_party/mako/mako/test/templates/read_unicode.html
 create mode 100644 third_party/mako/mako/test/templates/runtimeerr.html
 create mode 100644 third_party/mako/mako/test/templates/unicode_arguments.html
 create mode 100644 third_party/mako/mako/test/templates/unicode_code.html
 create mode 100644 third_party/mako/mako/test/templates/unicode_expr.html
 delete mode 100644 third_party/mako/mako/test/testing/dummy.cfg
 delete mode 100644 third_party/mako/mako/test/testing/test_config.py
 create mode 100644 third_party/mako/mako/test/util.py

diff --git a/third_party/mako/README.chromium b/third_party/mako/README.chromium
index 307da9d17272..7e8337ba4259 100644
--- a/third_party/mako/README.chromium
+++ b/third_party/mako/README.chromium
@@ -1,8 +1,8 @@
 Name: Mako Templates for Python
 Short Name: python-mako
 URL: https://www.makotemplates.org
-Version: 1.2.0
-Date: March 11, 2022
+Version: 1.1.4
+Date: Junuary 15, 2021
 License: MIT
 License File: NOT_SHIPPED
 Security Critical: no
@@ -10,11 +10,12 @@ Security Critical: no
 Description:
 Template engine in Python, used for code generation in Blink.
 
-Source: https://files.pythonhosted.org/packages/50/ec/1d687348f0954bda388bfd1330c158ba8d7dea4044fc160e74e080babdb9/Mako-1.2.0.tar.gz
-SHA-256: 9a7c7e922b87db3686210cf49d5d767033a41d4010b284e747682c92bddd8b39
+Source: https://files.pythonhosted.org/packages/5c/db/2d2d88b924aa4674a080aae83b59ea19d593250bfe5ed789947c21736785/Mako-1.1.4.tar.gz
+SHA-512: 4844c1d6c8d0d474b4ca4e1b31d3557747fc7e30f70a1976163a26b46b1b45c4c96ca6101fbef252b4e3bb4a61635d2a2c6d1c2933fde5b82bb1a1306f31ff84
+SHA-256: 17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab
 
 Local Modifications:
 * add DIR_METADATA, OWNERS, README.chromium, and .gitattributes files in this directory.
 * run following commands to pass PRESUBMIT.
-  $ rm -rf mako/doc/ mako/example/ mako/Mako.egg-info mako/PKG-INFO
+  $ rm -rf mako/doc/ mako/example/
   $ chmod -x mako/mako/cmd.py
diff --git a/third_party/mako/mako/LICENSE b/third_party/mako/mako/LICENSE
index 2342a9d3d028..1f835e94a1c7 100644
--- a/third_party/mako/mako/LICENSE
+++ b/third_party/mako/mako/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>.
+Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>.
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of
 this software and associated documentation files (the "Software"), to deal in
diff --git a/third_party/mako/mako/MANIFEST.in b/third_party/mako/mako/MANIFEST.in
index 25324e3c3cfc..2a2dc6c7096c 100644
--- a/third_party/mako/mako/MANIFEST.in
+++ b/third_party/mako/mako/MANIFEST.in
@@ -1,6 +1,6 @@
 recursive-include doc *.html *.css *.txt *.js *.png *.py Makefile *.rst *.mako
 recursive-include examples *.py *.xml *.mako *.myt *.kid *.tmpl
-recursive-include test *.py *.html *.mako *.cfg
+recursive-include test *.py *.html *.mako
 
 include README* AUTHORS LICENSE CHANGES* tox.ini
 
diff --git a/third_party/mako/mako/Mako.egg-info/PKG-INFO b/third_party/mako/mako/Mako.egg-info/PKG-INFO
new file mode 100644
index 000000000000..f69071cc95fb
--- /dev/null
+++ b/third_party/mako/mako/Mako.egg-info/PKG-INFO
@@ -0,0 +1,80 @@
+Metadata-Version: 2.1
+Name: Mako
+Version: 1.1.4
+Summary: A super-fast templating language that borrows the  best ideas from the existing templating languages.
+Home-page: https://www.makotemplates.org/
+Author: Mike Bayer
+Author-email: mike@zzzcomputing.com
+License: MIT
+Project-URL: Documentation, https://docs.makotemplates.org
+Project-URL: Issue Tracker, https://github.com/sqlalchemy/mako
+Description: =========================
+        Mako Templates for Python
+        =========================
+        
+        Mako is a template library written in Python. It provides a familiar, non-XML 
+        syntax which compiles into Python modules for maximum performance. Mako's 
+        syntax and API borrows from the best ideas of many others, including Django
+        templates, Cheetah, Myghty, and Genshi. Conceptually, Mako is an embedded 
+        Python (i.e. Python Server Page) language, which refines the familiar ideas
+        of componentized layout and inheritance to produce one of the most 
+        straightforward and flexible models available, while also maintaining close 
+        ties to Python calling and scoping semantics.
+        
+        Nutshell
+        ========
+        
+        ::
+        
+            <%inherit file="base.html"/>
+            <%
+                rows = [[v for v in range(0,10)] for row in range(0,10)]
+            %>
+            <table>
+                % for row in rows:
+                    ${makerow(row)}
+                % endfor
+            </table>
+        
+            <%def name="makerow(row)">
+                <tr>
+                % for name in row:
+                    <td>${name}</td>\
+                % endfor
+                </tr>
+            </%def>
+        
+        Philosophy
+        ===========
+        
+        Python is a great scripting language. Don't reinvent the wheel...your templates can handle it !
+        
+        Documentation
+        ==============
+        
+        See documentation for Mako at https://docs.makotemplates.org/en/latest/
+        
+        License
+        ========
+        
+        Mako is licensed under an MIT-style license (see LICENSE).
+        Other incorporated projects may be licensed under different licenses.
+        All licenses allow for non-commercial and commercial use.
+        
+Keywords: templates
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+Provides-Extra: babel
+Provides-Extra: lingua
diff --git a/third_party/mako/mako/Mako.egg-info/SOURCES.txt b/third_party/mako/mako/Mako.egg-info/SOURCES.txt
new file mode 100644
index 000000000000..f8d4f9ec5c92
--- /dev/null
+++ b/third_party/mako/mako/Mako.egg-info/SOURCES.txt
@@ -0,0 +1,161 @@
+AUTHORS
+CHANGES
+LICENSE
+MANIFEST.in
+README.rst
+setup.cfg
+setup.py
+tox.ini
+Mako.egg-info/PKG-INFO
+Mako.egg-info/SOURCES.txt
+Mako.egg-info/dependency_links.txt
+Mako.egg-info/entry_points.txt
+Mako.egg-info/not-zip-safe
+Mako.egg-info/requires.txt
+Mako.egg-info/top_level.txt
+doc/caching.html
+doc/changelog.html
+doc/defs.html
+doc/filtering.html
+doc/genindex.html
+doc/index.html
+doc/inheritance.html
+doc/namespaces.html
+doc/runtime.html
+doc/search.html
+doc/searchindex.js
+doc/syntax.html
+doc/unicode.html
+doc/usage.html
+doc/_static/basic.css
+doc/_static/changelog.css
+doc/_static/docs.css
+doc/_static/doctools.js
+doc/_static/documentation_options.js
+doc/_static/file.png
+doc/_static/jquery-3.5.1.js
+doc/_static/jquery.js
+doc/_static/language_data.js
+doc/_static/minus.png
+doc/_static/plus.png
+doc/_static/pygments.css
+doc/_static/searchtools.js
+doc/_static/sphinx_paramlinks.css
+doc/_static/underscore-1.3.1.js
+doc/_static/underscore.js
+doc/build/Makefile
+doc/build/caching.rst
+doc/build/changelog.rst
+doc/build/conf.py
+doc/build/defs.rst
+doc/build/filtering.rst
+doc/build/index.rst
+doc/build/inheritance.rst
+doc/build/namespaces.rst
+doc/build/requirements.txt
+doc/build/runtime.rst
+doc/build/syntax.rst
+doc/build/unicode.rst
+doc/build/usage.rst
+doc/build/unreleased/README.txt
+examples/bench/basic.py
+examples/bench/cheetah/footer.tmpl
+examples/bench/cheetah/header.tmpl
+examples/bench/cheetah/template.tmpl
+examples/bench/django/templatetags/__init__.py
+examples/bench/django/templatetags/bench.py
+examples/bench/kid/base.kid
+examples/bench/kid/template.kid
+examples/bench/myghty/base.myt
+examples/bench/myghty/template.myt
+examples/wsgi/run_wsgi.py
+mako/__init__.py
+mako/_ast_util.py
+mako/ast.py
+mako/cache.py
+mako/cmd.py
+mako/codegen.py
+mako/compat.py
+mako/exceptions.py
+mako/filters.py
+mako/lexer.py
+mako/lookup.py
+mako/parsetree.py
+mako/pygen.py
+mako/pyparser.py
+mako/runtime.py
+mako/template.py
+mako/util.py
+mako/ext/__init__.py
+mako/ext/autohandler.py
+mako/ext/babelplugin.py
+mako/ext/beaker_cache.py
+mako/ext/extract.py
+mako/ext/linguaplugin.py
+mako/ext/preprocessors.py
+mako/ext/pygmentplugin.py
+mako/ext/turbogears.py
+test/__init__.py
+test/sample_module_namespace.py
+test/test_ast.py
+test/test_block.py
+test/test_cache.py
+test/test_call.py
+test/test_cmd.py
+test/test_decorators.py
+test/test_def.py
+test/test_exceptions.py
+test/test_filters.py
+test/test_inheritance.py
+test/test_lexer.py
+test/test_lookup.py
+test/test_loop.py
+test/test_lru.py
+test/test_namespace.py
+test/test_pygen.py
+test/test_runtime.py
+test/test_template.py
+test/test_tgplugin.py
+test/test_util.py
+test/util.py
+test/ext/__init__.py
+test/ext/test_babelplugin.py
+test/ext/test_linguaplugin.py
+test/foo/__init__.py
+test/foo/mod_no_encoding.py
+test/foo/test_ns.py
+test/templates/badbom.html
+test/templates/bom.html
+test/templates/bommagic.html
+test/templates/chs_unicode.html
+test/templates/chs_unicode_py3k.html
+test/templates/chs_utf8.html
+test/templates/cmd_good.mako
+test/templates/cmd_runtime.mako
+test/templates/cmd_syntax.mako
+test/templates/crlf.html
+test/templates/gettext.mako
+test/templates/gettext_cp1251.mako
+test/templates/gettext_utf8.mako
+test/templates/index.html
+test/templates/internationalization.html
+test/templates/modtest.html
+test/templates/read_unicode.html
+test/templates/read_unicode_py3k.html
+test/templates/runtimeerr.html
+test/templates/runtimeerr_py3k.html
+test/templates/unicode.html
+test/templates/unicode_arguments.html
+test/templates/unicode_arguments_py3k.html
+test/templates/unicode_code.html
+test/templates/unicode_code_py3k.html
+test/templates/unicode_expr.html
+test/templates/unicode_expr_py3k.html
+test/templates/unicode_runtime_error.html
+test/templates/unicode_syntax_error.html
+test/templates/foo/modtest.html.py
+test/templates/othersubdir/foo.html
+test/templates/subdir/incl.html
+test/templates/subdir/index.html
+test/templates/subdir/modtest.html
+test/templates/subdir/foo/modtest.html.py
\ No newline at end of file
diff --git a/third_party/mako/mako/Mako.egg-info/dependency_links.txt b/third_party/mako/mako/Mako.egg-info/dependency_links.txt
new file mode 100644
index 000000000000..8b137891791f
--- /dev/null
+++ b/third_party/mako/mako/Mako.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/mako/mako/Mako.egg-info/entry_points.txt b/third_party/mako/mako/Mako.egg-info/entry_points.txt
new file mode 100644
index 000000000000..8e033c004015
--- /dev/null
+++ b/third_party/mako/mako/Mako.egg-info/entry_points.txt
@@ -0,0 +1,20 @@
+
+      [python.templating.engines]
+      mako = mako.ext.turbogears:TGPlugin
+
+      [pygments.lexers]
+      mako = mako.ext.pygmentplugin:MakoLexer
+      html+mako = mako.ext.pygmentplugin:MakoHtmlLexer
+      xml+mako = mako.ext.pygmentplugin:MakoXmlLexer
+      js+mako = mako.ext.pygmentplugin:MakoJavascriptLexer
+      css+mako = mako.ext.pygmentplugin:MakoCssLexer
+
+      [babel.extractors]
+      mako = mako.ext.babelplugin:extract [babel]
+
+      [lingua.extractors]
+      mako = mako.ext.linguaplugin:LinguaMakoExtractor [lingua]
+
+      [console_scripts]
+      mako-render = mako.cmd:cmdline
+      
\ No newline at end of file
diff --git a/third_party/mako/mako/Mako.egg-info/not-zip-safe b/third_party/mako/mako/Mako.egg-info/not-zip-safe
new file mode 100644
index 000000000000..8b137891791f
--- /dev/null
+++ b/third_party/mako/mako/Mako.egg-info/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/third_party/mako/mako/Mako.egg-info/requires.txt b/third_party/mako/mako/Mako.egg-info/requires.txt
new file mode 100644
index 000000000000..191643faa61c
--- /dev/null
+++ b/third_party/mako/mako/Mako.egg-info/requires.txt
@@ -0,0 +1,7 @@
+MarkupSafe>=0.9.2
+
+[babel]
+Babel
+
+[lingua]
+lingua
diff --git a/third_party/mako/mako/Mako.egg-info/top_level.txt b/third_party/mako/mako/Mako.egg-info/top_level.txt
new file mode 100644
index 000000000000..2951cdd49d33
--- /dev/null
+++ b/third_party/mako/mako/Mako.egg-info/top_level.txt
@@ -0,0 +1 @@
+mako
diff --git a/third_party/mako/mako/PKG-INFO b/third_party/mako/mako/PKG-INFO
new file mode 100644
index 000000000000..f69071cc95fb
--- /dev/null
+++ b/third_party/mako/mako/PKG-INFO
@@ -0,0 +1,80 @@
+Metadata-Version: 2.1
+Name: Mako
+Version: 1.1.4
+Summary: A super-fast templating language that borrows the  best ideas from the existing templating languages.
+Home-page: https://www.makotemplates.org/
+Author: Mike Bayer
+Author-email: mike@zzzcomputing.com
+License: MIT
+Project-URL: Documentation, https://docs.makotemplates.org
+Project-URL: Issue Tracker, https://github.com/sqlalchemy/mako
+Description: =========================
+        Mako Templates for Python
+        =========================
+        
+        Mako is a template library written in Python. It provides a familiar, non-XML 
+        syntax which compiles into Python modules for maximum performance. Mako's 
+        syntax and API borrows from the best ideas of many others, including Django
+        templates, Cheetah, Myghty, and Genshi. Conceptually, Mako is an embedded 
+        Python (i.e. Python Server Page) language, which refines the familiar ideas
+        of componentized layout and inheritance to produce one of the most 
+        straightforward and flexible models available, while also maintaining close 
+        ties to Python calling and scoping semantics.
+        
+        Nutshell
+        ========
+        
+        ::
+        
+            <%inherit file="base.html"/>
+            <%
+                rows = [[v for v in range(0,10)] for row in range(0,10)]
+            %>
+            <table>
+                % for row in rows:
+                    ${makerow(row)}
+                % endfor
+            </table>
+        
+            <%def name="makerow(row)">
+                <tr>
+                % for name in row:
+                    <td>${name}</td>\
+                % endfor
+                </tr>
+            </%def>
+        
+        Philosophy
+        ===========
+        
+        Python is a great scripting language. Don't reinvent the wheel...your templates can handle it !
+        
+        Documentation
+        ==============
+        
+        See documentation for Mako at https://docs.makotemplates.org/en/latest/
+        
+        License
+        ========
+        
+        Mako is licensed under an MIT-style license (see LICENSE).
+        Other incorporated projects may be licensed under different licenses.
+        All licenses allow for non-commercial and commercial use.
+        
+Keywords: templates
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+Provides-Extra: babel
+Provides-Extra: lingua
diff --git a/third_party/mako/mako/mako/__init__.py b/third_party/mako/mako/mako/__init__.py
index 5ae5501135aa..bd1a1c974e4c 100644
--- a/third_party/mako/mako/mako/__init__.py
+++ b/third_party/mako/mako/mako/__init__.py
@@ -1,8 +1,8 @@
 # mako/__init__.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
 
-__version__ = "1.2.0"
+__version__ = '1.1.4'
diff --git a/third_party/mako/mako/mako/_ast_util.py b/third_party/mako/mako/mako/_ast_util.py
index b8615335d6b2..bdcdbf691388 100644
--- a/third_party/mako/mako/mako/_ast_util.py
+++ b/third_party/mako/mako/mako/_ast_util.py
@@ -1,5 +1,5 @@
 # mako/_ast_util.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -47,6 +47,7 @@ from _ast import Sub
 from _ast import UAdd
 from _ast import USub
 
+from mako.compat import arg_stringname
 
 BOOLOP_SYMBOLS = {And: "and", Or: "or"}
 
@@ -93,7 +94,9 @@ def parse(expr, filename="<unknown>", mode="exec"):
 
 def iter_fields(node):
     """Iterate over all fields of a node, only yielding existing fields."""
-
+    # CPython 2.5 compat
+    if not hasattr(node, "_fields") or not node._fields:
+        return
     for field in node._fields:
         try:
             yield field, getattr(node, field)
@@ -101,7 +104,7 @@ def iter_fields(node):
             pass
 
 
-class NodeVisitor:
+class NodeVisitor(object):
 
     """
     Walks the abstract syntax tree and call visitor functions for every node
@@ -263,10 +266,10 @@ class SourceGenerator(NodeVisitor):
                 self.visit(default)
         if node.vararg is not None:
             write_comma()
-            self.write("*" + node.vararg.arg)
+            self.write("*" + arg_stringname(node.vararg))
         if node.kwarg is not None:
             write_comma()
-            self.write("**" + node.kwarg.arg)
+            self.write("**" + arg_stringname(node.kwarg))
 
     def decorators(self, node):
         for decorator in node.decorator_list:
diff --git a/third_party/mako/mako/mako/ast.py b/third_party/mako/mako/mako/ast.py
index f879e8b4bf60..cfae28062ce2 100644
--- a/third_party/mako/mako/mako/ast.py
+++ b/third_party/mako/mako/mako/ast.py
@@ -1,5 +1,5 @@
 # mako/ast.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -9,11 +9,12 @@ code, as well as generating Python from AST nodes"""
 
 import re
 
+from mako import compat
 from mako import exceptions
 from mako import pyparser
 
 
-class PythonCode:
+class PythonCode(object):
 
     """represents information about a string containing Python code"""
 
@@ -38,7 +39,7 @@ class PythonCode:
         # - AST is less likely to break with version changes
         # (for example, the behavior of co_names changed a little bit
         # in python version 2.5)
-        if isinstance(code, str):
+        if isinstance(code, compat.string_types):
             expr = pyparser.parse(code.lstrip(), "exec", **exception_kwargs)
         else:
             expr = code
@@ -47,7 +48,7 @@ class PythonCode:
         f.visit(expr)
 
 
-class ArgumentList:
+class ArgumentList(object):
 
     """parses a fragment of code as a comma-separated list of expressions"""
 
@@ -56,7 +57,7 @@ class ArgumentList:
         self.args = []
         self.declared_identifiers = set()
         self.undeclared_identifiers = set()
-        if isinstance(code, str):
+        if isinstance(code, compat.string_types):
             if re.match(r"\S", code) and not re.match(r",\s*$", code):
                 # if theres text and no trailing comma, insure its parsed
                 # as a tuple by adding a trailing comma
@@ -87,7 +88,7 @@ class PythonFragment(PythonCode):
         if not m:
             raise exceptions.CompileException(
                 "Fragment '%s' is not a partial control statement" % code,
-                **exception_kwargs,
+                **exception_kwargs
             )
         if m.group(3):
             code = code[: m.start(3)]
@@ -96,7 +97,7 @@ class PythonFragment(PythonCode):
             code = code + "pass"
         elif keyword == "try":
             code = code + "pass\nexcept:pass"
-        elif keyword in ["elif", "else"]:
+        elif keyword == "elif" or keyword == "else":
             code = "if False:pass\n" + code + "pass"
         elif keyword == "except":
             code = "try:pass\n" + code + "pass"
@@ -105,12 +106,12 @@ class PythonFragment(PythonCode):
         else:
             raise exceptions.CompileException(
                 "Unsupported control keyword: '%s'" % keyword,
-                **exception_kwargs,
+                **exception_kwargs
             )
-        super().__init__(code, **exception_kwargs)
+        super(PythonFragment, self).__init__(code, **exception_kwargs)
 
 
-class FunctionDecl:
+class FunctionDecl(object):
 
     """function declaration"""
 
@@ -123,13 +124,13 @@ class FunctionDecl:
         if not hasattr(self, "funcname"):
             raise exceptions.CompileException(
                 "Code '%s' is not a function declaration" % code,
-                **exception_kwargs,
+                **exception_kwargs
             )
         if not allow_kwargs and self.kwargs:
             raise exceptions.CompileException(
                 "'**%s' keyword argument not allowed here"
                 % self.kwargnames[-1],
-                **exception_kwargs,
+                **exception_kwargs
             )
 
     def get_argument_expressions(self, as_call=False):
@@ -199,4 +200,6 @@ class FunctionArgs(FunctionDecl):
     """the argument portion of a function declaration"""
 
     def __init__(self, code, **kwargs):
-        super().__init__("def ANON(%s):pass" % code, **kwargs)
+        super(FunctionArgs, self).__init__(
+            "def ANON(%s):pass" % code, **kwargs
+        )
diff --git a/third_party/mako/mako/mako/cache.py b/third_party/mako/mako/mako/cache.py
index d77be271159f..26aa93ee3898 100644
--- a/third_party/mako/mako/mako/cache.py
+++ b/third_party/mako/mako/mako/cache.py
@@ -1,9 +1,10 @@
 # mako/cache.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
+from mako import compat
 from mako import util
 
 _cache_plugins = util.PluginLoader("mako.cache")
@@ -12,7 +13,7 @@ register_plugin = _cache_plugins.register
 register_plugin("beaker", "mako.ext.beaker_cache", "BeakerCacheImpl")
 
 
-class Cache:
+class Cache(object):
 
     """Represents a data content cache made available to the module
     space of a specific :class:`.Template` object.
@@ -65,7 +66,7 @@ class Cache:
     def __init__(self, template, *args):
         # check for a stale template calling the
         # constructor
-        if isinstance(template, str) and args:
+        if isinstance(template, compat.string_types) and args:
             return
         self.template = template
         self.id = template.module.__name__
@@ -180,7 +181,7 @@ class Cache:
         return tmpl_kw
 
 
-class CacheImpl:
+class CacheImpl(object):
 
     """Provide a cache implementation for use by :class:`.Cache`."""
 
diff --git a/third_party/mako/mako/mako/cmd.py b/third_party/mako/mako/mako/cmd.py
index 7592fb279df1..c0f2c754ecd8 100644
--- a/third_party/mako/mako/mako/cmd.py
+++ b/third_party/mako/mako/mako/cmd.py
@@ -1,9 +1,10 @@
 # mako/cmd.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 from argparse import ArgumentParser
+import io
 from os.path import dirname
 from os.path import isfile
 import sys
@@ -84,14 +85,16 @@ def cmdline(argv=None):
         except:
             _exit()
 
-    kw = dict(varsplit(var) for var in options.var)
+    kw = dict([varsplit(var) for var in options.var])
     try:
         rendered = template.render(**kw)
     except:
         _exit()
     else:
         if output_file:
-            open(output_file, "wt", encoding=output_encoding).write(rendered)
+            io.open(output_file, "wt", encoding=output_encoding).write(
+                rendered
+            )
         else:
             sys.stdout.write(rendered)
 
diff --git a/third_party/mako/mako/mako/codegen.py b/third_party/mako/mako/mako/codegen.py
index c897f0fff178..a9ae55b847f1 100644
--- a/third_party/mako/mako/mako/codegen.py
+++ b/third_party/mako/mako/mako/codegen.py
@@ -1,5 +1,5 @@
 # mako/codegen.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -12,6 +12,7 @@ import re
 import time
 
 from mako import ast
+from mako import compat
 from mako import exceptions
 from mako import filters
 from mako import parsetree
@@ -24,8 +25,8 @@ MAGIC_NUMBER = 10
 # names which are hardwired into the
 # template and are not accessed via the
 # context itself
-TOPLEVEL_DECLARED = {"UNDEFINED", "STOP_RENDERING"}
-RESERVED_NAMES = {"context", "loop"}.union(TOPLEVEL_DECLARED)
+TOPLEVEL_DECLARED = set(["UNDEFINED", "STOP_RENDERING"])
+RESERVED_NAMES = set(["context", "loop"]).union(TOPLEVEL_DECLARED)
 
 
 def compile(  # noqa
@@ -38,12 +39,20 @@ def compile(  # noqa
     future_imports=None,
     source_encoding=None,
     generate_magic_comment=True,
+    disable_unicode=False,
     strict_undefined=False,
     enable_loop=True,
     reserved_names=frozenset(),
 ):
     """Generate module source code given a parsetree node,
-    uri, and optional source filename"""
+      uri, and optional source filename"""
+
+    # if on Py2K, push the "source_encoding" string to be
+    # a bytestring itself, as we will be embedding it into
+    # the generated source and we don't want to coerce the
+    # result into a unicode object, in "disable_unicode" mode
+    if not compat.py3k and isinstance(source_encoding, compat.text_type):
+        source_encoding = source_encoding.encode(source_encoding)
 
     buf = util.FastEncodingBuffer()
 
@@ -59,6 +68,7 @@ def compile(  # noqa
             future_imports,
             source_encoding,
             generate_magic_comment,
+            disable_unicode,
             strict_undefined,
             enable_loop,
             reserved_names,
@@ -68,7 +78,7 @@ def compile(  # noqa
     return buf.getvalue()
 
 
-class _CompileContext:
+class _CompileContext(object):
     def __init__(
         self,
         uri,
@@ -79,6 +89,7 @@ class _CompileContext:
         future_imports,
         source_encoding,
         generate_magic_comment,
+        disable_unicode,
         strict_undefined,
         enable_loop,
         reserved_names,
@@ -91,15 +102,16 @@ class _CompileContext:
         self.future_imports = future_imports
         self.source_encoding = source_encoding
         self.generate_magic_comment = generate_magic_comment
+        self.disable_unicode = disable_unicode
         self.strict_undefined = strict_undefined
         self.enable_loop = enable_loop
         self.reserved_names = reserved_names
 
 
-class _GenerateRenderMethod:
+class _GenerateRenderMethod(object):
 
     """A template visitor object which generates the
-    full module source for a template.
+       full module source for a template.
 
     """
 
@@ -184,7 +196,7 @@ class _GenerateRenderMethod:
 
         self.compiler.pagetag = None
 
-        class FindTopLevel:
+        class FindTopLevel(object):
             def visitInheritTag(s, node):
                 inherit.append(node)
 
@@ -380,7 +392,7 @@ class _GenerateRenderMethod:
                 identifiers = self.compiler.identifiers.branch(node)
                 self.in_def = True
 
-                class NSDefVisitor:
+                class NSDefVisitor(object):
                     def visitDefTag(s, node):
                         s.visitDefOrBase(node)
 
@@ -392,7 +404,7 @@ class _GenerateRenderMethod:
                             raise exceptions.CompileException(
                                 "Can't put anonymous blocks inside "
                                 "<%namespace>",
-                                **node.exception_kwargs,
+                                **node.exception_kwargs
                             )
                         self.write_inline_def(node, identifiers, nested=False)
                         export.append(node.funcname)
@@ -469,7 +481,7 @@ class _GenerateRenderMethod:
         """
 
         # collection of all defs available to us in this scope
-        comp_idents = {c.funcname: c for c in identifiers.defs}
+        comp_idents = dict([(c.funcname, c) for c in identifiers.defs])
         to_write = set()
 
         # write "context.get()" for all variables we are going to
@@ -702,7 +714,7 @@ class _GenerateRenderMethod:
         toplevel=False,
     ):
         """write a post-function decorator to replace a rendering
-        callable with a cached version of itself."""
+            callable with a cached version of itself."""
 
         self.printer.writeline("__M_%s = %s" % (name, name))
         cachekey = node_or_pagetag.parsed_attributes.get(
@@ -782,6 +794,8 @@ class _GenerateRenderMethod:
         def locate_encode(name):
             if re.match(r"decode\..+", name):
                 return "filters." + name
+            elif self.compiler.disable_unicode:
+                return filters.NON_UNICODE_ESCAPES.get(name, name)
             else:
                 return filters.DEFAULT_ESCAPES.get(name, name)
 
@@ -845,11 +859,11 @@ class _GenerateRenderMethod:
             #          and end control lines, and
             #    3) any control line with no content other than comments
             if not children or (
-                all(
+                compat.all(
                     isinstance(c, (parsetree.Comment, parsetree.ControlLine))
                     for c in children
                 )
-                and all(
+                and compat.all(
                     (node.is_ternary(c.keyword) or c.isend)
                     for c in children
                     if isinstance(c, parsetree.ControlLine)
@@ -955,7 +969,7 @@ class _GenerateRenderMethod:
 
         self.identifier_stack.append(body_identifiers)
 
-        class DefVisitor:
+        class DefVisitor(object):
             def visitDefTag(s, node):
                 s.visitDefOrBase(node)
 
@@ -1011,7 +1025,7 @@ class _GenerateRenderMethod:
         )
 
 
-class _Identifiers:
+class _Identifiers(object):
 
     """tracks the status of identifier names as template code is rendered."""
 
@@ -1084,7 +1098,7 @@ class _Identifiers:
 
     def branch(self, node, **kwargs):
         """create a new Identifiers for a new Node, with
-        this Identifiers as the parent."""
+          this Identifiers as the parent."""
 
         return _Identifiers(self.compiler, node, self, **kwargs)
 
@@ -1109,7 +1123,7 @@ class _Identifiers:
 
     def check_declared(self, node):
         """update the state of this Identifiers with the undeclared
-        and declared identifiers of the given node."""
+            and declared identifiers of the given node."""
 
         for ident in node.undeclared_identifiers():
             if ident != "context" and ident not in self.declared.union(
@@ -1156,7 +1170,7 @@ class _Identifiers:
             raise exceptions.CompileException(
                 "%%def or %%block named '%s' already "
                 "exists in this template." % node.funcname,
-                **node.exception_kwargs,
+                **node.exception_kwargs
             )
 
     def visitDefTag(self, node):
@@ -1186,7 +1200,7 @@ class _Identifiers:
                 raise exceptions.CompileException(
                     "Named block '%s' not allowed inside of def '%s'"
                     % (node.name, self.node.name),
-                    **node.exception_kwargs,
+                    **node.exception_kwargs
                 )
             elif isinstance(
                 self.node, (parsetree.CallTag, parsetree.CallNamespaceTag)
@@ -1194,7 +1208,7 @@ class _Identifiers:
                 raise exceptions.CompileException(
                     "Named block '%s' not allowed inside of <%%call> tag"
                     % (node.name,),
-                    **node.exception_kwargs,
+                    **node.exception_kwargs
                 )
 
         for ident in node.undeclared_identifiers():
@@ -1279,7 +1293,7 @@ def mangle_mako_loop(node, printer):
     return text
 
 
-class LoopVariable:
+class LoopVariable(object):
 
     """A node visitor which looks for the name 'loop' within undeclared
     identifiers."""
diff --git a/third_party/mako/mako/mako/compat.py b/third_party/mako/mako/mako/compat.py
index 68bc03b1dd7c..06bb8d99f588 100644
--- a/third_party/mako/mako/mako/compat.py
+++ b/third_party/mako/mako/mako/compat.py
@@ -1,17 +1,19 @@
 # mako/compat.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
 import collections
-from importlib import util
 import inspect
 import sys
 
+py3k = sys.version_info >= (3, 0)
+py2k = sys.version_info < (3,)
+py27 = sys.version_info >= (2, 7)
+jython = sys.platform.startswith("java")
 win32 = sys.platform.startswith("win")
 pypy = hasattr(sys, "pypy_version_info")
-py38 = sys.version_info >= (3, 8)
 
 ArgSpec = collections.namedtuple(
     "ArgSpec", ["args", "varargs", "keywords", "defaults"]
@@ -24,15 +26,15 @@ def inspect_getargspec(func):
     if inspect.ismethod(func):
         func = func.__func__
     if not inspect.isfunction(func):
-        raise TypeError(f"{func!r} is not a Python function")
+        raise TypeError("{!r} is not a Python function".format(func))
 
     co = func.__code__
     if not inspect.iscode(co):
-        raise TypeError(f"{co!r} is not a code object")
+        raise TypeError("{!r} is not a code object".format(co))
 
     nargs = co.co_argcount
     names = co.co_varnames
-    nkwargs = co.co_kwonlyargcount
+    nkwargs = co.co_kwonlyargcount if py3k else 0
     args = list(names[:nargs])
 
     nargs += nkwargs
@@ -47,30 +49,129 @@ def inspect_getargspec(func):
     return ArgSpec(args, varargs, varkw, func.__defaults__)
 
 
-def load_module(module_id, path):
-    spec = util.spec_from_file_location(module_id, path)
-    module = util.module_from_spec(spec)
-    spec.loader.exec_module(module)
-    return module
+if py3k:
+    from io import StringIO
+    import builtins as compat_builtins
+    from urllib.parse import quote_plus, unquote_plus
+    from html.entities import codepoint2name, name2codepoint
+
+    string_types = (str,)
+    binary_type = bytes
+    text_type = str
+
+    from io import BytesIO as byte_buffer
+
+    def u(s):
+        return s
+
+    def b(s):
+        return s.encode("latin-1")
+
+    def octal(lit):
+        return eval("0o" + lit)
+
+
+else:
+    import __builtin__ as compat_builtins  # noqa
+
+    try:
+        from cStringIO import StringIO
+    except:
+        from StringIO import StringIO
+
+    byte_buffer = StringIO
+
+    from urllib import quote_plus, unquote_plus  # noqa
+    from htmlentitydefs import codepoint2name, name2codepoint  # noqa
+
+    string_types = (basestring,)  # noqa
+    binary_type = str
+    text_type = unicode  # noqa
+
+    def u(s):
+        return unicode(s, "utf-8")  # noqa
+
+    def b(s):
+        return s
+
+    def octal(lit):
+        return eval("0" + lit)
+
+
+if py3k:
+    from importlib import machinery, util
+
+    if hasattr(util, 'module_from_spec'):
+        # Python 3.5+
+        def load_module(module_id, path):
+            spec = util.spec_from_file_location(module_id, path)
+            module = util.module_from_spec(spec)
+            spec.loader.exec_module(module)
+            return module
+    else:
+        def load_module(module_id, path):
+            module = machinery.SourceFileLoader(module_id, path).load_module()
+            del sys.modules[module_id]
+            return module
+
+else:
+    import imp
+
+    def load_module(module_id, path):
+        fp = open(path, "rb")
+        try:
+            module = imp.load_source(module_id, path, fp)
+            del sys.modules[module_id]
+            return module
+        finally:
+            fp.close()
+
+
+if py3k:
+
+    def reraise(tp, value, tb=None, cause=None):
+        if cause is not None:
+            value.__cause__ = cause
+        if value.__traceback__ is not tb:
+            raise value.with_traceback(tb)
+        raise value
+
+
+else:
+    exec(
+        "def reraise(tp, value, tb=None, cause=None):\n"
+        "    raise tp, value, tb\n"
+    )
 
 
 def exception_as():
     return sys.exc_info()[1]
 
 
+all = all  # noqa
+
+
 def exception_name(exc):
     return exc.__class__.__name__
 
 
-if py38:
-    from importlib import metadata as importlib_metadata
-else:
-    import importlib_metadata  # noqa
+################################################
+# cross-compatible metaclass implementation
+# Copyright (c) 2010-2012 Benjamin Peterson
+def with_metaclass(meta, base=object):
+    """Create a base class with a metaclass."""
+    return meta("%sBase" % meta.__name__, (base,), {})
+
+
+################################################
 
 
-def importlib_metadata_get(group):
-    ep = importlib_metadata.entry_points()
-    if hasattr(ep, "select"):
-        return ep.select(group=group)
+def arg_stringname(func_arg):
+    """Gets the string name of a kwarg or vararg
+    In Python3.4 a function's args are
+    of _ast.arg type not _ast.name
+    """
+    if hasattr(func_arg, "arg"):
+        return func_arg.arg
     else:
-        return ep.get(group, ())
+        return str(func_arg)
diff --git a/third_party/mako/mako/mako/exceptions.py b/third_party/mako/mako/mako/exceptions.py
index a0a5feca77d8..ea7b20dbddb2 100644
--- a/third_party/mako/mako/mako/exceptions.py
+++ b/third_party/mako/mako/mako/exceptions.py
@@ -1,5 +1,5 @@
 # mako/exceptions.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -68,7 +68,7 @@ class TopLevelLookupException(TemplateLookupException):
     pass
 
 
-class RichTraceback:
+class RichTraceback(object):
 
     """Pull the current exception from the ``sys`` traceback and extracts
     Mako-specific template information.
@@ -106,7 +106,7 @@ class RichTraceback:
     def _init_message(self):
         """Find a unicode representation of self.error"""
         try:
-            self.message = str(self.error)
+            self.message = compat.text_type(self.error)
         except UnicodeError:
             try:
                 self.message = str(self.error)
@@ -114,8 +114,8 @@ class RichTraceback:
                 # Fallback to args as neither unicode nor
                 # str(Exception(u'\xe6')) work in Python < 2.6
                 self.message = self.error.args[0]
-        if not isinstance(self.message, str):
-            self.message = str(self.message, "ascii", "replace")
+        if not isinstance(self.message, compat.text_type):
+            self.message = compat.text_type(self.message, "ascii", "replace")
 
     def _get_reformatted_records(self, records):
         for rec in records:
@@ -139,7 +139,8 @@ class RichTraceback:
 
     @property
     def reverse_traceback(self):
-        """Return the same data as traceback, except in reverse order."""
+        """Return the same data as traceback, except in reverse order.
+        """
 
         return list(self._get_reformatted_records(self.reverse_records))
 
@@ -169,6 +170,17 @@ class RichTraceback:
                     )
                 except KeyError:
                     # A normal .py file (not a Template)
+                    if not compat.py3k:
+                        try:
+                            fp = open(filename, "rb")
+                            encoding = util.parse_encoding(fp)
+                            fp.close()
+                        except IOError:
+                            encoding = None
+                        if encoding:
+                            line = line.decode(encoding)
+                        else:
+                            line = line.decode("ascii", "replace")
                     new_trcback.append(
                         (
                             filename,
@@ -224,12 +236,13 @@ class RichTraceback:
                 if new_trcback:
                     try:
                         # A normal .py file (not a Template)
-                        with open(new_trcback[-1][0], "rb") as fp:
-                            encoding = util.parse_encoding(fp)
-                            if not encoding:
-                                encoding = "utf-8"
-                            fp.seek(0)
-                            self.source = fp.read()
+                        fp = open(new_trcback[-1][0], "rb")
+                        encoding = util.parse_encoding(fp)
+                        if compat.py3k and not encoding:
+                            encoding = "utf-8"
+                        fp.seek(0)
+                        self.source = fp.read()
+                        fp.close()
                         if encoding:
                             self.source = self.source.decode(encoding)
                     except IOError:
diff --git a/third_party/mako/mako/mako/ext/autohandler.py b/third_party/mako/mako/mako/ext/autohandler.py
index e8fdac89f615..8b1324ef0084 100644
--- a/third_party/mako/mako/mako/ext/autohandler.py
+++ b/third_party/mako/mako/mako/ext/autohandler.py
@@ -1,5 +1,5 @@
 # ext/autohandler.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
diff --git a/third_party/mako/mako/mako/ext/babelplugin.py b/third_party/mako/mako/mako/ext/babelplugin.py
index f015ec254770..76bbc5b03f8b 100644
--- a/third_party/mako/mako/mako/ext/babelplugin.py
+++ b/third_party/mako/mako/mako/ext/babelplugin.py
@@ -1,10 +1,10 @@
 # ext/babelplugin.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
-"""gettext message extraction via Babel: https://pypi.org/project/Babel/"""
+"""gettext message extraction via Babel: http://babel.edgewall.org/"""
 from babel.messages.extract import extract_python
 
 from mako.ext.extract import MessageExtractor
@@ -15,12 +15,12 @@ class BabelMakoExtractor(MessageExtractor):
         self.keywords = keywords
         self.options = options
         self.config = {
-            "comment-tags": " ".join(comment_tags),
+            "comment-tags": u" ".join(comment_tags),
             "encoding": options.get(
                 "input_encoding", options.get("encoding", None)
             ),
         }
-        super().__init__()
+        super(BabelMakoExtractor, self).__init__()
 
     def __call__(self, fileobj):
         return self.process_file(fileobj)
@@ -54,4 +54,5 @@ def extract(fileobj, keywords, comment_tags, options):
     :rtype: ``iterator``
     """
     extractor = BabelMakoExtractor(keywords, comment_tags, options)
-    yield from extractor(fileobj)
+    for message in extractor(fileobj):
+        yield message
diff --git a/third_party/mako/mako/mako/ext/beaker_cache.py b/third_party/mako/mako/mako/ext/beaker_cache.py
index a40b09cf488a..f65ce43ae83c 100644
--- a/third_party/mako/mako/mako/ext/beaker_cache.py
+++ b/third_party/mako/mako/mako/ext/beaker_cache.py
@@ -1,5 +1,5 @@
 # ext/beaker_cache.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -40,7 +40,7 @@ class BeakerCacheImpl(CacheImpl):
                 _beaker_cache = cache.template.cache_args["manager"]
             else:
                 _beaker_cache = beaker_cache.CacheManager()
-        super().__init__(cache)
+        super(BeakerCacheImpl, self).__init__(cache)
 
     def _get_cache(self, **kw):
         expiretime = kw.pop("timeout", None)
diff --git a/third_party/mako/mako/mako/ext/extract.py b/third_party/mako/mako/mako/ext/extract.py
index 74d067d8495b..ad2348a52339 100644
--- a/third_party/mako/mako/mako/ext/extract.py
+++ b/third_party/mako/mako/mako/ext/extract.py
@@ -1,25 +1,23 @@
 # ext/extract.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
-from io import BytesIO
-from io import StringIO
 import re
 
+from mako import compat
 from mako import lexer
 from mako import parsetree
 
 
-class MessageExtractor:
-    use_bytes = True
-
+class MessageExtractor(object):
     def process_file(self, fileobj):
         template_node = lexer.Lexer(
             fileobj.read(), input_encoding=self.config["encoding"]
         ).parse()
-        yield from self.extract_nodes(template_node.get_children())
+        for extracted in self.extract_nodes(template_node.get_children()):
+            yield extracted
 
     def extract_nodes(self, nodes):
         translator_comments = []
@@ -92,7 +90,7 @@ class MessageExtractor:
                 comment[1] for comment in translator_comments
             ]
 
-            if isinstance(code, str) and self.use_bytes:
+            if isinstance(code, compat.text_type):
                 code = code.encode(input_encoding, "backslashreplace")
 
             used_translator_comments = False
@@ -101,10 +99,7 @@ class MessageExtractor:
             # input string of the input is non-ascii)
             # Also, because we added it, we have to subtract one from
             # node.lineno
-            if self.use_bytes:
-                code = BytesIO(b"\n" + code)
-            else:
-                code = StringIO("\n" + code)
+            code = compat.byte_buffer(compat.b("\n") + code)
 
             for message in self.process_python(
                 code, node.lineno - 1, translator_strings
@@ -117,7 +112,8 @@ class MessageExtractor:
             in_translator_comments = False
 
             if child_nodes:
-                yield from self.extract_nodes(child_nodes)
+                for extracted in self.extract_nodes(child_nodes):
+                    yield extracted
 
     @staticmethod
     def _split_comment(lineno, comment):
diff --git a/third_party/mako/mako/mako/ext/linguaplugin.py b/third_party/mako/mako/mako/ext/linguaplugin.py
index 4cce6262b72b..c40fa748422c 100644
--- a/third_party/mako/mako/mako/ext/linguaplugin.py
+++ b/third_party/mako/mako/mako/ext/linguaplugin.py
@@ -1,23 +1,23 @@
 # ext/linguaplugin.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
-import contextlib
 import io
 
 from lingua.extractors import Extractor
 from lingua.extractors import get_extractor
 from lingua.extractors import Message
 
+from mako import compat
 from mako.ext.extract import MessageExtractor
 
 
 class LinguaMakoExtractor(Extractor, MessageExtractor):
+
     """Mako templates"""
 
-    use_bytes = False
     extensions = [".mako"]
     default_config = {"encoding": "utf-8", "comment-tags": ""}
 
@@ -26,21 +26,29 @@ class LinguaMakoExtractor(Extractor, MessageExtractor):
         self.filename = filename
         self.python_extractor = get_extractor("x.py")
         if fileobj is None:
-            ctx = open(filename, "r")
+            fileobj = open(filename, "rb")
+            must_close = True
         else:
-            ctx = contextlib.nullcontext(fileobj)
-        with ctx as file_:
-            yield from self.process_file(file_)
+            must_close = False
+        try:
+            for message in self.process_file(fileobj):
+                yield message
+        finally:
+            if must_close:
+                fileobj.close()
 
     def process_python(self, code, code_lineno, translator_strings):
         source = code.getvalue().strip()
-        if source.endswith(":"):
-            if source in ("try:", "else:") or source.startswith("except"):
-                source = ""  # Ignore try/except and else
-            elif source.startswith("elif"):
+        if source.endswith(compat.b(":")):
+            if source in (
+                compat.b("try:"),
+                compat.b("else:"),
+            ) or source.startswith(compat.b("except")):
+                source = compat.b("")  # Ignore try/except and else
+            elif source.startswith(compat.b("elif")):
                 source = source[2:]  # Replace "elif" with "if"
-            source += "pass"
-        code = io.StringIO(source)
+            source += compat.b("pass")
+        code = io.BytesIO(source)
         for msg in self.python_extractor(
             self.filename, self.options, code, code_lineno - 1
         ):
@@ -50,7 +58,7 @@ class LinguaMakoExtractor(Extractor, MessageExtractor):
                     msg.msgid,
                     msg.msgid_plural,
                     msg.flags,
-                    " ".join(translator_strings + [msg.comment]),
+                    compat.u(" ").join(translator_strings + [msg.comment]),
                     msg.tcomment,
                     msg.location,
                 )
diff --git a/third_party/mako/mako/mako/ext/preprocessors.py b/third_party/mako/mako/mako/ext/preprocessors.py
index 6855eeb43780..9cc062148202 100644
--- a/third_party/mako/mako/mako/ext/preprocessors.py
+++ b/third_party/mako/mako/mako/ext/preprocessors.py
@@ -1,5 +1,5 @@
 # ext/preprocessors.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
diff --git a/third_party/mako/mako/mako/ext/pygmentplugin.py b/third_party/mako/mako/mako/ext/pygmentplugin.py
index 38d6a71ba04e..943a67a49cb0 100644
--- a/third_party/mako/mako/mako/ext/pygmentplugin.py
+++ b/third_party/mako/mako/mako/ext/pygmentplugin.py
@@ -1,5 +1,5 @@
 # ext/pygmentplugin.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -25,6 +25,8 @@ from pygments.token import Other
 from pygments.token import String
 from pygments.token import Text
 
+from mako import compat
+
 
 class MakoLexer(RegexLexer):
     name = "Mako"
@@ -106,7 +108,7 @@ class MakoHtmlLexer(DelegatingLexer):
     aliases = ["html+mako"]
 
     def __init__(self, **options):
-        super().__init__(HtmlLexer, MakoLexer, **options)
+        super(MakoHtmlLexer, self).__init__(HtmlLexer, MakoLexer, **options)
 
 
 class MakoXmlLexer(DelegatingLexer):
@@ -114,7 +116,7 @@ class MakoXmlLexer(DelegatingLexer):
     aliases = ["xml+mako"]
 
     def __init__(self, **options):
-        super().__init__(XmlLexer, MakoLexer, **options)
+        super(MakoXmlLexer, self).__init__(XmlLexer, MakoLexer, **options)
 
 
 class MakoJavascriptLexer(DelegatingLexer):
@@ -122,7 +124,9 @@ class MakoJavascriptLexer(DelegatingLexer):
     aliases = ["js+mako", "javascript+mako"]
 
     def __init__(self, **options):
-        super().__init__(JavascriptLexer, MakoLexer, **options)
+        super(MakoJavascriptLexer, self).__init__(
+            JavascriptLexer, MakoLexer, **options
+        )
 
 
 class MakoCssLexer(DelegatingLexer):
@@ -130,7 +134,7 @@ class MakoCssLexer(DelegatingLexer):
     aliases = ["css+mako"]
 
     def __init__(self, **options):
-        super().__init__(CssLexer, MakoLexer, **options)
+        super(MakoCssLexer, self).__init__(CssLexer, MakoLexer, **options)
 
 
 pygments_html_formatter = HtmlFormatter(
@@ -140,7 +144,10 @@ pygments_html_formatter = HtmlFormatter(
 
 def syntax_highlight(filename="", language=None):
     mako_lexer = MakoLexer()
-    python_lexer = Python3Lexer()
+    if compat.py3k:
+        python_lexer = Python3Lexer()
+    else:
+        python_lexer = PythonLexer()
     if filename.startswith("memory:") or language == "mako":
         return lambda string: highlight(
             string, mako_lexer, pygments_html_formatter
diff --git a/third_party/mako/mako/mako/ext/turbogears.py b/third_party/mako/mako/mako/ext/turbogears.py
index 413d9f74021e..722a6b4b4055 100644
--- a/third_party/mako/mako/mako/ext/turbogears.py
+++ b/third_party/mako/mako/mako/ext/turbogears.py
@@ -1,5 +1,5 @@
 # ext/turbogears.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -9,7 +9,7 @@ from mako.lookup import TemplateLookup
 from mako.template import Template
 
 
-class TGPlugin:
+class TGPlugin(object):
 
     """TurboGears compatible Template Plugin."""
 
@@ -51,7 +51,7 @@ class TGPlugin:
     def render(
         self, info, format="html", fragment=False, template=None  # noqa
     ):
-        if isinstance(template, str):
+        if isinstance(template, compat.string_types):
             template = self.load_template(template)
 
         # Load extra vars func if provided
diff --git a/third_party/mako/mako/mako/filters.py b/third_party/mako/mako/mako/filters.py
index 26edd8ee8877..0ae33ff48649 100644
--- a/third_party/mako/mako/mako/filters.py
+++ b/third_party/mako/mako/mako/filters.py
@@ -1,19 +1,18 @@
 # mako/filters.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
 
 
 import codecs
-from html.entities import codepoint2name
-from html.entities import name2codepoint
 import re
-from urllib.parse import quote_plus
 
-import markupsafe
-
-html_escape = markupsafe.escape
+from mako import compat
+from mako.compat import codepoint2name
+from mako.compat import name2codepoint
+from mako.compat import quote_plus
+from mako.compat import unquote_plus
 
 xml_escapes = {
     "&": "&amp;",
@@ -23,6 +22,27 @@ xml_escapes = {
     "'": "&#39;",  # also &apos; in html-only
 }
 
+# XXX: &quot; is valid in HTML and XML
+#      &apos; is not valid HTML, but is valid XML
+
+
+def legacy_html_escape(s):
+    """legacy HTML escape for non-unicode mode."""
+    s = s.replace("&", "&amp;")
+    s = s.replace(">", "&gt;")
+    s = s.replace("<", "&lt;")
+    s = s.replace('"', "&#34;")
+    s = s.replace("'", "&#39;")
+    return s
+
+
+try:
+    import markupsafe
+
+    html_escape = markupsafe.escape
+except ImportError:
+    html_escape = legacy_html_escape
+
 
 def xml_escape(string):
     return re.sub(r'([&<"\'>])', lambda m: xml_escapes[m.group()], string)
@@ -34,19 +54,31 @@ def url_escape(string):
     return quote_plus(string)
 
 
+def legacy_url_escape(string):
+    # convert into a list of octets
+    return quote_plus(string)
+
+
+def url_unescape(string):
+    text = unquote_plus(string)
+    if not is_ascii_str(text):
+        text = text.decode("utf8")
+    return text
+
+
 def trim(string):
     return string.strip()
 
 
-class Decode:
+class Decode(object):
     def __getattr__(self, key):
         def decode(x):
-            if isinstance(x, str):
+            if isinstance(x, compat.text_type):
                 return x
-            elif not isinstance(x, bytes):
+            elif not isinstance(x, compat.binary_type):
                 return decode(str(x))
             else:
-                return str(x, encoding=key)
+                return compat.text_type(x, encoding=key)
 
         return decode
 
@@ -54,11 +86,24 @@ class Decode:
 decode = Decode()
 
 
-class XMLEntityEscaper:
+_ASCII_re = re.compile(r"\A[\x00-\x7f]*\Z")
+
+
+def is_ascii_str(text):
+    return isinstance(text, str) and _ASCII_re.match(text)
+
+
+################################################################
+
+
+class XMLEntityEscaper(object):
     def __init__(self, codepoint2name, name2codepoint):
-        self.codepoint2entity = {
-            c: str("&%s;" % n) for c, n in codepoint2name.items()
-        }
+        self.codepoint2entity = dict(
+            [
+                (c, compat.text_type("&%s;" % n))
+                for c, n in codepoint2name.items()
+            ]
+        )
         self.name2codepoint = name2codepoint
 
     def escape_entities(self, text):
@@ -66,7 +111,7 @@ class XMLEntityEscaper:
 
         Only characters corresponding to a named entity are replaced.
         """
-        return str(text).translate(self.codepoint2entity)
+        return compat.text_type(text).translate(self.codepoint2entity)
 
     def __escape(self, m):
         codepoint = ord(m.group())
@@ -86,7 +131,9 @@ class XMLEntityEscaper:
 
         The return value is guaranteed to be ASCII.
         """
-        return self.__escapable.sub(self.__escape, str(text)).encode("ascii")
+        return self.__escapable.sub(
+            self.__escape, compat.text_type(text)
+        ).encode("ascii")
 
     # XXX: This regexp will not match all valid XML entity names__.
     # (It punts on details involving involving CombiningChars and Extenders.)
@@ -136,28 +183,37 @@ def htmlentityreplace_errors(ex):
     characters with HTML entities, or, if no HTML entity exists for
     the character, XML character references::
 
-        >>> 'The cost was \u20ac12.'.encode('latin1', 'htmlentityreplace')
+        >>> u'The cost was \u20ac12.'.encode('latin1', 'htmlentityreplace')
         'The cost was &euro;12.'
     """
     if isinstance(ex, UnicodeEncodeError):
         # Handle encoding errors
         bad_text = ex.object[ex.start : ex.end]
         text = _html_entities_escaper.escape(bad_text)
-        return (str(text), ex.end)
+        return (compat.text_type(text), ex.end)
     raise ex
 
 
 codecs.register_error("htmlentityreplace", htmlentityreplace_errors)
 
 
+# TODO: options to make this dynamic per-compilation will be added in a later
+# release
 DEFAULT_ESCAPES = {
     "x": "filters.xml_escape",
     "h": "filters.html_escape",
     "u": "filters.url_escape",
     "trim": "filters.trim",
     "entity": "filters.html_entities_escape",
-    "unicode": "str",
+    "unicode": "unicode",
     "decode": "decode",
     "str": "str",
     "n": "n",
 }
+
+if compat.py3k:
+    DEFAULT_ESCAPES.update({"unicode": "str"})
+
+NON_UNICODE_ESCAPES = DEFAULT_ESCAPES.copy()
+NON_UNICODE_ESCAPES["h"] = "filters.legacy_html_escape"
+NON_UNICODE_ESCAPES["u"] = "filters.legacy_url_escape"
diff --git a/third_party/mako/mako/mako/lexer.py b/third_party/mako/mako/mako/lexer.py
index 527c4b51820e..6226e2684608 100644
--- a/third_party/mako/mako/mako/lexer.py
+++ b/third_party/mako/mako/mako/lexer.py
@@ -1,5 +1,5 @@
 # mako/lexer.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -9,6 +9,7 @@
 import codecs
 import re
 
+from mako import compat
 from mako import exceptions
 from mako import parsetree
 from mako.pygen import adjust_whitespace
@@ -16,9 +17,14 @@ from mako.pygen import adjust_whitespace
 _regexp_cache = {}
 
 
-class Lexer:
+class Lexer(object):
     def __init__(
-        self, text, filename=None, input_encoding=None, preprocessor=None
+        self,
+        text,
+        filename=None,
+        disable_unicode=False,
+        input_encoding=None,
+        preprocessor=None,
     ):
         self.text = text
         self.filename = filename
@@ -30,8 +36,14 @@ class Lexer:
         self.tag = []
         self.control_line = []
         self.ternary_stack = []
+        self.disable_unicode = disable_unicode
         self.encoding = input_encoding
 
+        if compat.py3k and disable_unicode:
+            raise exceptions.UnsupportedError(
+                "Mako for Python 3 does not " "support disabling Unicode"
+            )
+
         if preprocessor is None:
             self.preprocessor = []
         elif not hasattr(preprocessor, "__iter__"):
@@ -54,7 +66,10 @@ class Lexer:
         try:
             reg = _regexp_cache[(regexp, flags)]
         except KeyError:
-            reg = re.compile(regexp, flags) if flags else re.compile(regexp)
+            if flags:
+                reg = re.compile(regexp, flags)
+            else:
+                reg = re.compile(regexp)
             _regexp_cache[(regexp, flags)] = reg
 
         return self.match_reg(reg)
@@ -72,7 +87,10 @@ class Lexer:
         match = reg.match(self.text, self.match_position)
         if match:
             (start, end) = match.span()
-            self.match_position = end + 1 if end == start else end
+            if end == start:
+                self.match_position = end + 1
+            else:
+                self.match_position = end
             self.matched_lineno = self.lineno
             lines = re.findall(r"\n", self.text[mp : self.match_position])
             cp = mp - 1
@@ -80,6 +98,10 @@ class Lexer:
                 cp -= 1
             self.matched_charpos = mp - cp
             self.lineno += len(lines)
+            # print "MATCHED:", match.group(0), "LINE START:",
+            # self.matched_lineno, "LINE END:", self.lineno
+        # print "MATCH:", regexp, "\n", self.text[mp : mp + 15], \
+        #          (match and "TRUE" or "FALSE")
         return match
 
     def parse_until_text(self, watch_nesting, *text):
@@ -139,15 +161,12 @@ class Lexer:
         if self.control_line:
             control_frame = self.control_line[-1]
             control_frame.nodes.append(node)
-            if (
-                not (
-                    isinstance(node, parsetree.ControlLine)
-                    and control_frame.is_ternary(node.keyword)
-                )
-                and self.ternary_stack
-                and self.ternary_stack[-1]
+            if not (
+                isinstance(node, parsetree.ControlLine)
+                and control_frame.is_ternary(node.keyword)
             ):
-                self.ternary_stack[-1][-1].nodes.append(node)
+                if self.ternary_stack and self.ternary_stack[-1]:
+                    self.ternary_stack[-1][-1].nodes.append(node)
         if isinstance(node, parsetree.Tag):
             if len(self.tag):
                 node.parent = self.tag[-1]
@@ -169,18 +188,18 @@ class Lexer:
                 raise exceptions.SyntaxException(
                     "Keyword '%s' not a legal ternary for keyword '%s'"
                     % (node.keyword, self.control_line[-1].keyword),
-                    **self.exception_kwargs,
+                    **self.exception_kwargs
                 )
 
     _coding_re = re.compile(r"#.*coding[:=]\s*([-\w.]+).*\r?\n")
 
     def decode_raw_stream(self, text, decode_raw, known_encoding, filename):
         """given string/unicode or bytes/string, determine encoding
-        from magic encoding comment, return body as unicode
-        or raw if decode_raw=False
+           from magic encoding comment, return body as unicode
+           or raw if decode_raw=False
 
         """
-        if isinstance(text, str):
+        if isinstance(text, compat.text_type):
             m = self._coding_re.match(text)
             encoding = m and m.group(1) or known_encoding or "utf-8"
             return encoding, text
@@ -200,7 +219,11 @@ class Lexer:
                 )
         else:
             m = self._coding_re.match(text.decode("utf-8", "ignore"))
-            parsed_encoding = m.group(1) if m else known_encoding or "utf-8"
+            if m:
+                parsed_encoding = m.group(1)
+            else:
+                parsed_encoding = known_encoding or "utf-8"
+
         if decode_raw:
             try:
                 text = text.decode(parsed_encoding)
@@ -218,7 +241,7 @@ class Lexer:
 
     def parse(self):
         self.encoding, self.text = self.decode_raw_stream(
-            self.text, True, self.encoding, self.filename
+            self.text, not self.disable_unicode, self.encoding, self.filename
         )
 
         for preproc in self.preprocessor:
@@ -253,13 +276,12 @@ class Lexer:
 
             if self.match_position > self.textlength:
                 break
-            # TODO: no coverage here
-            raise exceptions.MakoException("assertion failed")
+            raise exceptions.CompileException("assertion failed")
 
         if len(self.tag):
             raise exceptions.SyntaxException(
                 "Unclosed tag: <%%%s>" % self.tag[-1].keyword,
-                **self.exception_kwargs,
+                **self.exception_kwargs
             )
         if len(self.control_line):
             raise exceptions.SyntaxException(
@@ -290,34 +312,35 @@ class Lexer:
             re.I | re.S | re.X,
         )
 
-        if not match:
+        if match:
+            keyword, attr, isend = match.groups()
+            self.keyword = keyword
+            attributes = {}
+            if attr:
+                for att in re.findall(
+                    r"\s*(\w+)\s*=\s*(?:'([^']*)'|\"([^\"]*)\")", attr
+                ):
+                    key, val1, val2 = att
+                    text = val1 or val2
+                    text = text.replace("\r\n", "\n")
+                    attributes[key] = text
+            self.append_node(parsetree.Tag, keyword, attributes)
+            if isend:
+                self.tag.pop()
+            else:
+                if keyword == "text":
+                    match = self.match(r"(.*?)(?=\</%text>)", re.S)
+                    if not match:
+                        raise exceptions.SyntaxException(
+                            "Unclosed tag: <%%%s>" % self.tag[-1].keyword,
+                            **self.exception_kwargs
+                        )
+                    self.append_node(parsetree.Text, match.group(1))
+                    return self.match_tag_end()
+            return True
+        else:
             return False
 
-        keyword, attr, isend = match.groups()
-        self.keyword = keyword
-        attributes = {}
-        if attr:
-            for att in re.findall(
-                r"\s*(\w+)\s*=\s*(?:'([^']*)'|\"([^\"]*)\")", attr
-            ):
-                key, val1, val2 = att
-                text = val1 or val2
-                text = text.replace("\r\n", "\n")
-                attributes[key] = text
-        self.append_node(parsetree.Tag, keyword, attributes)
-        if isend:
-            self.tag.pop()
-        elif keyword == "text":
-            match = self.match(r"(.*?)(?=\</%text>)", re.S)
-            if not match:
-                raise exceptions.SyntaxException(
-                    "Unclosed tag: <%%%s>" % self.tag[-1].keyword,
-                    **self.exception_kwargs,
-                )
-            self.append_node(parsetree.Text, match.group(1))
-            return self.match_tag_end()
-        return True
-
     def match_tag_end(self):
         match = self.match(r"\</%[\t ]*(.+?)[\t ]*>")
         if match:
@@ -325,13 +348,13 @@ class Lexer:
                 raise exceptions.SyntaxException(
                     "Closing tag without opening tag: </%%%s>"
                     % match.group(1),
-                    **self.exception_kwargs,
+                    **self.exception_kwargs
                 )
             elif self.tag[-1].keyword != match.group(1):
                 raise exceptions.SyntaxException(
                     "Closing tag </%%%s> does not match tag: <%%%s>"
                     % (match.group(1), self.tag[-1].keyword),
-                    **self.exception_kwargs,
+                    **self.exception_kwargs
                 )
             self.tag.pop()
             return True
@@ -340,14 +363,14 @@ class Lexer:
 
     def match_end(self):
         match = self.match(r"\Z", re.S)
-        if not match:
-            return False
-
-        string = match.group()
-        if string:
-            return string
+        if match:
+            string = match.group()
+            if string:
+                return string
+            else:
+                return True
         else:
-            return True
+            return False
 
     def match_text(self):
         match = self.match(
@@ -399,62 +422,63 @@ class Lexer:
 
     def match_expression(self):
         match = self.match(r"\${")
-        if not match:
-            return False
-
-        line, pos = self.matched_lineno, self.matched_charpos
-        text, end = self.parse_until_text(True, r"\|", r"}")
-        if end == "|":
-            escapes, end = self.parse_until_text(True, r"}")
+        if match:
+            line, pos = self.matched_lineno, self.matched_charpos
+            text, end = self.parse_until_text(True, r"\|", r"}")
+            if end == "|":
+                escapes, end = self.parse_until_text(True, r"}")
+            else:
+                escapes = ""
+            text = text.replace("\r\n", "\n")
+            self.append_node(
+                parsetree.Expression,
+                text,
+                escapes.strip(),
+                lineno=line,
+                pos=pos,
+            )
+            return True
         else:
-            escapes = ""
-        text = text.replace("\r\n", "\n")
-        self.append_node(
-            parsetree.Expression,
-            text,
-            escapes.strip(),
-            lineno=line,
-            pos=pos,
-        )
-        return True
+            return False
 
     def match_control_line(self):
         match = self.match(
-            r"(?<=^)[\t ]*(%(?!%)|##)[\t ]*((?:(?:\\\r?\n)|[^\r\n])*)"
+            r"(?<=^)[\t ]*(%(?!%)|##)[\t ]*((?:(?:\\r?\n)|[^\r\n])*)"
             r"(?:\r?\n|\Z)",
             re.M,
         )
-        if not match:
-            return False
-
-        operator = match.group(1)
-        text = match.group(2)
-        if operator == "%":
-            m2 = re.match(r"(end)?(\w+)\s*(.*)", text)
-            if not m2:
-                raise exceptions.SyntaxException(
-                    "Invalid control line: '%s'" % text,
-                    **self.exception_kwargs,
-                )
-            isend, keyword = m2.group(1, 2)
-            isend = isend is not None
-
-            if isend:
-                if not len(self.control_line):
-                    raise exceptions.SyntaxException(
-                        "No starting keyword '%s' for '%s'" % (keyword, text),
-                        **self.exception_kwargs,
-                    )
-                elif self.control_line[-1].keyword != keyword:
+        if match:
+            operator = match.group(1)
+            text = match.group(2)
+            if operator == "%":
+                m2 = re.match(r"(end)?(\w+)\s*(.*)", text)
+                if not m2:
                     raise exceptions.SyntaxException(
-                        "Keyword '%s' doesn't match keyword '%s'"
-                        % (text, self.control_line[-1].keyword),
-                        **self.exception_kwargs,
+                        "Invalid control line: '%s'" % text,
+                        **self.exception_kwargs
                     )
-            self.append_node(parsetree.ControlLine, keyword, isend, text)
+                isend, keyword = m2.group(1, 2)
+                isend = isend is not None
+
+                if isend:
+                    if not len(self.control_line):
+                        raise exceptions.SyntaxException(
+                            "No starting keyword '%s' for '%s'"
+                            % (keyword, text),
+                            **self.exception_kwargs
+                        )
+                    elif self.control_line[-1].keyword != keyword:
+                        raise exceptions.SyntaxException(
+                            "Keyword '%s' doesn't match keyword '%s'"
+                            % (text, self.control_line[-1].keyword),
+                            **self.exception_kwargs
+                        )
+                self.append_node(parsetree.ControlLine, keyword, isend, text)
+            else:
+                self.append_node(parsetree.Comment, text)
+            return True
         else:
-            self.append_node(parsetree.Comment, text)
-        return True
+            return False
 
     def match_comment(self):
         """matches the multiline version of a comment"""
diff --git a/third_party/mako/mako/mako/lookup.py b/third_party/mako/mako/mako/lookup.py
index 7afe242b7638..476326d4d2c4 100644
--- a/third_party/mako/mako/mako/lookup.py
+++ b/third_party/mako/mako/mako/lookup.py
@@ -1,5 +1,5 @@
 # mako/lookup.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -8,14 +8,18 @@ import os
 import posixpath
 import re
 import stat
-import threading
 
 from mako import exceptions
 from mako import util
 from mako.template import Template
 
+try:
+    import threading
+except:
+    import dummy_threading as threading
 
-class TemplateCollection:
+
+class TemplateCollection(object):
 
     """Represent a collection of :class:`.Template` objects,
     identifiable via URI.
@@ -30,7 +34,7 @@ class TemplateCollection:
     :class:`.TemplateCollection` is an abstract class,
     with the usual default implementation being :class:`.TemplateLookup`.
 
-    """
+     """
 
     def has_template(self, uri):
         """Return ``True`` if this :class:`.TemplateLookup` is
@@ -64,7 +68,7 @@ class TemplateCollection:
 
     def filename_to_uri(self, uri, filename):
         """Convert the given ``filename`` to a URI relative to
-        this :class:`.TemplateCollection`."""
+           this :class:`.TemplateCollection`."""
 
         return uri
 
@@ -157,6 +161,8 @@ class TemplateLookup(TemplateCollection):
         collection_size=-1,
         format_exceptions=False,
         error_handler=None,
+        disable_unicode=False,
+        bytestring_passthrough=False,
         output_encoding=None,
         encoding_errors="strict",
         cache_args=None,
@@ -201,6 +207,8 @@ class TemplateLookup(TemplateCollection):
             "format_exceptions": format_exceptions,
             "error_handler": error_handler,
             "include_error_handler": include_error_handler,
+            "disable_unicode": disable_unicode,
+            "bytestring_passthrough": bytestring_passthrough,
             "output_encoding": output_encoding,
             "cache_impl": cache_impl,
             "encoding_errors": encoding_errors,
@@ -241,7 +249,7 @@ class TemplateLookup(TemplateCollection):
                 return self._check(uri, self._collection[uri])
             else:
                 return self._collection[uri]
-        except KeyError as e:
+        except KeyError:
             u = re.sub(r"^\/+", "", uri)
             for dir_ in self.directories:
                 # make sure the path seperators are posix - os.altsep is empty
@@ -252,8 +260,8 @@ class TemplateLookup(TemplateCollection):
                     return self._load(srcfile, uri)
             else:
                 raise exceptions.TopLevelLookupException(
-                    "Can't locate template for uri %r" % uri
-                ) from e
+                    "Cant locate template for uri %r" % uri
+                )
 
     def adjust_uri(self, uri, relativeto):
         """Adjust the given ``uri`` based on the given relative URI."""
@@ -262,19 +270,20 @@ class TemplateLookup(TemplateCollection):
         if key in self._uri_cache:
             return self._uri_cache[key]
 
-        if uri[0] == "/":
-            v = self._uri_cache[key] = uri
-        elif relativeto is not None:
-            v = self._uri_cache[key] = posixpath.join(
-                posixpath.dirname(relativeto), uri
-            )
+        if uri[0] != "/":
+            if relativeto is not None:
+                v = self._uri_cache[key] = posixpath.join(
+                    posixpath.dirname(relativeto), uri
+                )
+            else:
+                v = self._uri_cache[key] = "/" + uri
         else:
-            v = self._uri_cache[key] = "/" + uri
+            v = self._uri_cache[key] = uri
         return v
 
     def filename_to_uri(self, filename):
         """Convert the given ``filename`` to a URI relative to
-        this :class:`.TemplateCollection`."""
+           this :class:`.TemplateCollection`."""
 
         try:
             return self._uri_cache[filename]
@@ -285,7 +294,7 @@ class TemplateLookup(TemplateCollection):
 
     def _relativeize(self, filename):
         """Return the portion of a filename that is 'relative'
-        to the directories in this lookup.
+           to the directories in this lookup.
 
         """
 
@@ -315,7 +324,7 @@ class TemplateLookup(TemplateCollection):
                     filename=posixpath.normpath(filename),
                     lookup=self,
                     module_filename=module_filename,
-                    **self.template_args,
+                    **self.template_args
                 )
                 return template
             except:
@@ -333,15 +342,16 @@ class TemplateLookup(TemplateCollection):
 
         try:
             template_stat = os.stat(template.filename)
-            if template.module._modified_time >= template_stat[stat.ST_MTIME]:
+            if template.module._modified_time < template_stat[stat.ST_MTIME]:
+                self._collection.pop(uri, None)
+                return self._load(template.filename, uri)
+            else:
                 return template
-            self._collection.pop(uri, None)
-            return self._load(template.filename, uri)
-        except OSError as e:
+        except OSError:
             self._collection.pop(uri, None)
             raise exceptions.TemplateLookupException(
-                "Can't locate template for uri %r" % uri
-            ) from e
+                "Cant locate template for uri %r" % uri
+            )
 
     def put_string(self, uri, text):
         """Place a new :class:`.Template` object into this
diff --git a/third_party/mako/mako/mako/parsetree.py b/third_party/mako/mako/mako/parsetree.py
index 2135769f0693..801e48a7f4b1 100644
--- a/third_party/mako/mako/mako/parsetree.py
+++ b/third_party/mako/mako/mako/parsetree.py
@@ -1,5 +1,5 @@
 # mako/parsetree.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -9,12 +9,13 @@
 import re
 
 from mako import ast
+from mako import compat
 from mako import exceptions
 from mako import filters
 from mako import util
 
 
-class Node:
+class Node(object):
 
     """base class for a Node in the parse tree."""
 
@@ -50,7 +51,7 @@ class TemplateNode(Node):
     """a 'container' node that stores the overall collection of nodes."""
 
     def __init__(self, filename):
-        super().__init__("", 0, 0, filename)
+        super(TemplateNode, self).__init__("", 0, 0, filename)
         self.nodes = []
         self.page_attributes = {}
 
@@ -79,7 +80,7 @@ class ControlLine(Node):
     has_loop_context = False
 
     def __init__(self, keyword, isend, text, **kwargs):
-        super().__init__(**kwargs)
+        super(ControlLine, self).__init__(**kwargs)
         self.text = text
         self.keyword = keyword
         self.isend = isend
@@ -106,13 +107,11 @@ class ControlLine(Node):
         """return true if the given keyword is a ternary keyword
         for this ControlLine"""
 
-        cases = {
-            "if": {"else", "elif"},
-            "try": {"except", "finally"},
-            "for": {"else"},
-        }
-
-        return keyword in cases.get(self.keyword, set())
+        return keyword in {
+            "if": set(["else", "elif"]),
+            "try": set(["except", "finally"]),
+            "for": set(["else"]),
+        }.get(self.keyword, [])
 
     def __repr__(self):
         return "ControlLine(%r, %r, %r, %r)" % (
@@ -124,10 +123,11 @@ class ControlLine(Node):
 
 
 class Text(Node):
+
     """defines plain text in the template."""
 
     def __init__(self, content, **kwargs):
-        super().__init__(**kwargs)
+        super(Text, self).__init__(**kwargs)
         self.content = content
 
     def __repr__(self):
@@ -135,6 +135,7 @@ class Text(Node):
 
 
 class Code(Node):
+
     """defines a Python code block, either inline or module level.
 
     e.g.::
@@ -152,7 +153,7 @@ class Code(Node):
     """
 
     def __init__(self, text, ismodule, **kwargs):
-        super().__init__(**kwargs)
+        super(Code, self).__init__(**kwargs)
         self.text = text
         self.ismodule = ismodule
         self.code = ast.PythonCode(text, **self.exception_kwargs)
@@ -172,6 +173,7 @@ class Code(Node):
 
 
 class Comment(Node):
+
     """defines a comment line.
 
     # this is a comment
@@ -179,7 +181,7 @@ class Comment(Node):
     """
 
     def __init__(self, text, **kwargs):
-        super().__init__(**kwargs)
+        super(Comment, self).__init__(**kwargs)
         self.text = text
 
     def __repr__(self):
@@ -187,6 +189,7 @@ class Comment(Node):
 
 
 class Expression(Node):
+
     """defines an inline expression.
 
     ${x+y}
@@ -194,7 +197,7 @@ class Expression(Node):
     """
 
     def __init__(self, text, escapes, **kwargs):
-        super().__init__(**kwargs)
+        super(Expression, self).__init__(**kwargs)
         self.text = text
         self.escapes = escapes
         self.escapes_code = ast.ArgumentList(escapes, **self.exception_kwargs)
@@ -207,7 +210,7 @@ class Expression(Node):
         # TODO: make the "filter" shortcut list configurable at parse/gen time
         return self.code.undeclared_identifiers.union(
             self.escapes_code.undeclared_identifiers.difference(
-                filters.DEFAULT_ESCAPES
+                set(filters.DEFAULT_ESCAPES.keys())
             )
         ).difference(self.code.declared_identifiers)
 
@@ -220,6 +223,7 @@ class Expression(Node):
 
 
 class _TagMeta(type):
+
     """metaclass to allow Tag to produce a subclass according to
     its keyword"""
 
@@ -228,7 +232,7 @@ class _TagMeta(type):
     def __init__(cls, clsname, bases, dict_):
         if getattr(cls, "__keyword__", None) is not None:
             cls._classmap[cls.__keyword__] = cls
-        super().__init__(clsname, bases, dict_)
+        super(_TagMeta, cls).__init__(clsname, bases, dict_)
 
     def __call__(cls, keyword, attributes, **kwargs):
         if ":" in keyword:
@@ -250,7 +254,7 @@ class _TagMeta(type):
         return type.__call__(cls, keyword, attributes, **kwargs)
 
 
-class Tag(Node, metaclass=_TagMeta):
+class Tag(compat.with_metaclass(_TagMeta, Node)):
     """abstract base class for tags.
 
     e.g.::
@@ -272,7 +276,7 @@ class Tag(Node, metaclass=_TagMeta):
         expressions,
         nonexpressions,
         required,
-        **kwargs,
+        **kwargs
     ):
         r"""construct a new Tag instance.
 
@@ -293,20 +297,17 @@ class Tag(Node, metaclass=_TagMeta):
          other arguments passed to the Node superclass (lineno, pos)
 
         """
-        super().__init__(**kwargs)
+        super(Tag, self).__init__(**kwargs)
         self.keyword = keyword
         self.attributes = attributes
         self._parse_attributes(expressions, nonexpressions)
         missing = [r for r in required if r not in self.parsed_attributes]
         if len(missing):
             raise exceptions.CompileException(
-                (
-                    "Missing attribute(s): %s"
-                    % ",".join(repr(m) for m in missing)
-                ),
-                **self.exception_kwargs,
+                "Missing attribute(s): %s"
+                % ",".join([repr(m) for m in missing]),
+                **self.exception_kwargs
             )
-
         self.parent = None
         self.nodes = []
 
@@ -338,22 +339,23 @@ class Tag(Node, metaclass=_TagMeta):
                             code.undeclared_identifiers
                         )
                         expr.append("(%s)" % m.group(1))
-                    elif x:
-                        expr.append(repr(x))
+                    else:
+                        if x:
+                            expr.append(repr(x))
                 self.parsed_attributes[key] = " + ".join(expr) or repr("")
             elif key in nonexpressions:
                 if re.search(r"\${.+?}", self.attributes[key]):
                     raise exceptions.CompileException(
-                        "Attribute '%s' in tag '%s' does not allow embedded "
+                        "Attibute '%s' in tag '%s' does not allow embedded "
                         "expressions" % (key, self.keyword),
-                        **self.exception_kwargs,
+                        **self.exception_kwargs
                     )
                 self.parsed_attributes[key] = repr(self.attributes[key])
             else:
                 raise exceptions.CompileException(
                     "Invalid attribute for tag '%s': '%s'"
                     % (self.keyword, key),
-                    **self.exception_kwargs,
+                    **self.exception_kwargs
                 )
         self.expression_undeclared_identifiers = undeclared_identifiers
 
@@ -377,13 +379,13 @@ class IncludeTag(Tag):
     __keyword__ = "include"
 
     def __init__(self, keyword, attributes, **kwargs):
-        super().__init__(
+        super(IncludeTag, self).__init__(
             keyword,
             attributes,
             ("file", "import", "args"),
             (),
             ("file",),
-            **kwargs,
+            **kwargs
         )
         self.page_args = ast.PythonCode(
             "__DUMMY(%s)" % attributes.get("args", ""), **self.exception_kwargs
@@ -394,22 +396,24 @@ class IncludeTag(Tag):
 
     def undeclared_identifiers(self):
         identifiers = self.page_args.undeclared_identifiers.difference(
-            {"__DUMMY"}
+            set(["__DUMMY"])
         ).difference(self.page_args.declared_identifiers)
-        return identifiers.union(super().undeclared_identifiers())
+        return identifiers.union(
+            super(IncludeTag, self).undeclared_identifiers()
+        )
 
 
 class NamespaceTag(Tag):
     __keyword__ = "namespace"
 
     def __init__(self, keyword, attributes, **kwargs):
-        super().__init__(
+        super(NamespaceTag, self).__init__(
             keyword,
             attributes,
             ("file",),
             ("name", "inheritable", "import", "module"),
             (),
-            **kwargs,
+            **kwargs
         )
 
         self.name = attributes.get("name", "__anon_%s" % hex(abs(id(self))))
@@ -417,12 +421,12 @@ class NamespaceTag(Tag):
             raise exceptions.CompileException(
                 "'name' and/or 'import' attributes are required "
                 "for <%namespace>",
-                **self.exception_kwargs,
+                **self.exception_kwargs
             )
         if "file" in attributes and "module" in attributes:
             raise exceptions.CompileException(
                 "<%namespace> may only have one of 'file' or 'module'",
-                **self.exception_kwargs,
+                **self.exception_kwargs
             )
 
     def declared_identifiers(self):
@@ -433,7 +437,9 @@ class TextTag(Tag):
     __keyword__ = "text"
 
     def __init__(self, keyword, attributes, **kwargs):
-        super().__init__(keyword, attributes, (), ("filter"), (), **kwargs)
+        super(TextTag, self).__init__(
+            keyword, attributes, (), ("filter"), (), **kwargs
+        )
         self.filter_args = ast.ArgumentList(
             attributes.get("filter", ""), **self.exception_kwargs
         )
@@ -452,13 +458,13 @@ class DefTag(Tag):
             c for c in attributes if c.startswith("cache_")
         ]
 
-        super().__init__(
+        super(DefTag, self).__init__(
             keyword,
             attributes,
             expressions,
             ("name", "filter", "decorator"),
             ("name",),
-            **kwargs,
+            **kwargs
         )
         name = attributes["name"]
         if re.match(r"^[\w_]+$", name):
@@ -515,19 +521,19 @@ class BlockTag(Tag):
             c for c in attributes if c.startswith("cache_")
         ]
 
-        super().__init__(
+        super(BlockTag, self).__init__(
             keyword,
             attributes,
             expressions,
             ("name", "filter", "decorator"),
             (),
-            **kwargs,
+            **kwargs
         )
         name = attributes.get("name")
         if name and not re.match(r"^[\w_]+$", name):
             raise exceptions.CompileException(
                 "%block may not specify an argument signature",
-                **self.exception_kwargs,
+                **self.exception_kwargs
             )
         if not name and attributes.get("args", None):
             raise exceptions.CompileException(
@@ -571,7 +577,7 @@ class CallTag(Tag):
     __keyword__ = "call"
 
     def __init__(self, keyword, attributes, **kwargs):
-        super().__init__(
+        super(CallTag, self).__init__(
             keyword, attributes, ("args"), ("expr",), ("expr",), **kwargs
         )
         self.expression = attributes["expr"]
@@ -591,25 +597,26 @@ class CallTag(Tag):
 
 class CallNamespaceTag(Tag):
     def __init__(self, namespace, defname, attributes, **kwargs):
-        super().__init__(
+        super(CallNamespaceTag, self).__init__(
             namespace + ":" + defname,
             attributes,
             tuple(attributes.keys()) + ("args",),
             (),
             (),
-            **kwargs,
+            **kwargs
         )
 
         self.expression = "%s.%s(%s)" % (
             namespace,
             defname,
             ",".join(
-                "%s=%s" % (k, v)
-                for k, v in self.parsed_attributes.items()
-                if k != "args"
+                [
+                    "%s=%s" % (k, v)
+                    for k, v in self.parsed_attributes.items()
+                    if k != "args"
+                ]
             ),
         )
-
         self.code = ast.PythonCode(self.expression, **self.exception_kwargs)
         self.body_decl = ast.FunctionArgs(
             attributes.get("args", ""), **self.exception_kwargs
@@ -628,7 +635,7 @@ class InheritTag(Tag):
     __keyword__ = "inherit"
 
     def __init__(self, keyword, attributes, **kwargs):
-        super().__init__(
+        super(InheritTag, self).__init__(
             keyword, attributes, ("file",), (), ("file",), **kwargs
         )
 
@@ -644,7 +651,9 @@ class PageTag(Tag):
             "enable_loop",
         ] + [c for c in attributes if c.startswith("cache_")]
 
-        super().__init__(keyword, attributes, expressions, (), (), **kwargs)
+        super(PageTag, self).__init__(
+            keyword, attributes, expressions, (), (), **kwargs
+        )
         self.body_decl = ast.FunctionArgs(
             attributes.get("args", ""), **self.exception_kwargs
         )
diff --git a/third_party/mako/mako/mako/pygen.py b/third_party/mako/mako/mako/pygen.py
index 46b0b52ffece..947721f1a5cf 100644
--- a/third_party/mako/mako/mako/pygen.py
+++ b/third_party/mako/mako/mako/pygen.py
@@ -1,5 +1,5 @@
 # mako/pygen.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -11,7 +11,7 @@ import re
 from mako import exceptions
 
 
-class PythonPrinter:
+class PythonPrinter(object):
     def __init__(self, stream):
         # indentation counter
         self.indent = 0
@@ -96,19 +96,18 @@ class PythonPrinter:
         is_comment = line and len(line) and line[0] == "#"
 
         # see if this line should decrease the indentation level
-        if (
-            not is_comment
-            and (not hastext or self._is_unindentor(line))
-            and self.indent > 0
-        ):
-            self.indent -= 1
-            # if the indent_detail stack is empty, the user
-            # probably put extra closures - the resulting
-            # module wont compile.
-            if len(self.indent_detail) == 0:
-                # TODO: no coverage here
-                raise exceptions.MakoException("Too many whitespace closures")
-            self.indent_detail.pop()
+        if not is_comment and (not hastext or self._is_unindentor(line)):
+
+            if self.indent > 0:
+                self.indent -= 1
+                # if the indent_detail stack is empty, the user
+                # probably put extra closures - the resulting
+                # module wont compile.
+                if len(self.indent_detail) == 0:
+                    raise exceptions.SyntaxException(
+                        "Too many whitespace closures"
+                    )
+                self.indent_detail.pop()
 
         if line is None:
             return
@@ -168,10 +167,13 @@ class PythonPrinter:
         # if the current line doesnt have one of the "unindentor" keywords,
         # return False
         match = re.match(r"^\s*(else|elif|except|finally).*\:", line)
-        # if True, whitespace matches up, we have a compound indentor,
+        if not match:
+            return False
+
+        # whitespace matches up, we have a compound indentor,
         # and this line has an unindentor, this
         # is probably good enough
-        return bool(match)
+        return True
 
         # should we decide that its not good enough, heres
         # more stuff to check.
@@ -216,7 +218,11 @@ class PythonPrinter:
 
         current_state = self.backslashed or self.triplequoted
 
-        self.backslashed = bool(re.search(r"\\$", line))
+        if re.search(r"\\$", line):
+            self.backslashed = True
+        else:
+            self.backslashed = False
+
         triples = len(re.findall(r"\"\"\"|\'\'\'", line))
         if triples == 1 or triples % 2 != 0:
             self.triplequoted = not self.triplequoted
diff --git a/third_party/mako/mako/mako/pyparser.py b/third_party/mako/mako/mako/pyparser.py
index 5c55505ba002..b16672d60c7d 100644
--- a/third_party/mako/mako/mako/pyparser.py
+++ b/third_party/mako/mako/mako/pyparser.py
@@ -1,5 +1,5 @@
 # mako/pyparser.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -18,13 +18,22 @@ from mako import _ast_util
 from mako import compat
 from mako import exceptions
 from mako import util
+from mako.compat import arg_stringname
 
-# words that cannot be assigned to (notably
-# smaller than the total keys in __builtins__)
-reserved = {"True", "False", "None", "print"}
+if compat.py3k:
+    # words that cannot be assigned to (notably
+    # smaller than the total keys in __builtins__)
+    reserved = set(["True", "False", "None", "print"])
 
-# the "id" attribute on a function node
-arg_id = operator.attrgetter("arg")
+    # the "id" attribute on a function node
+    arg_id = operator.attrgetter("arg")
+else:
+    # words that cannot be assigned to (notably
+    # smaller than the total keys in __builtins__)
+    reserved = set(["True", "False", "None"])
+
+    # the "id" attribute on a function node
+    arg_id = operator.attrgetter("id")
 
 util.restore__ast(_ast)
 
@@ -34,7 +43,7 @@ def parse(code, mode="exec", **exception_kwargs):
 
     try:
         return _ast_util.parse(code, "<unknown>", mode)
-    except Exception as e:
+    except Exception:
         raise exceptions.SyntaxException(
             "(%s) %s (%r)"
             % (
@@ -42,8 +51,8 @@ def parse(code, mode="exec", **exception_kwargs):
                 compat.exception_as(),
                 code[0:50],
             ),
-            **exception_kwargs,
-        ) from e
+            **exception_kwargs
+        )
 
 
 class FindIdentifiers(_ast_util.NodeVisitor):
@@ -76,13 +85,18 @@ class FindIdentifiers(_ast_util.NodeVisitor):
             self.visit(n)
         self.in_assign_targets = in_a
 
-    def visit_ExceptHandler(self, node):
-        if node.name is not None:
-            self._add_declared(node.name)
-        if node.type is not None:
-            self.visit(node.type)
-        for statement in node.body:
-            self.visit(statement)
+    if compat.py3k:
+
+        # ExceptHandler is in Python 2, but this block only works in
+        # Python 3 (and is required there)
+
+        def visit_ExceptHandler(self, node):
+            if node.name is not None:
+                self._add_declared(node.name)
+            if node.type is not None:
+                self.visit(node.type)
+            for statement in node.body:
+                self.visit(statement)
 
     def visit_Lambda(self, node, *args):
         self._visit_function(node, True)
@@ -94,7 +108,8 @@ class FindIdentifiers(_ast_util.NodeVisitor):
     def _expand_tuples(self, args):
         for arg in args:
             if isinstance(arg, _ast.Tuple):
-                yield from arg.elts
+                for n in arg.elts:
+                    yield n
             else:
                 yield arg
 
@@ -155,15 +170,15 @@ class FindIdentifiers(_ast_util.NodeVisitor):
         for name in node.names:
             if name.asname is not None:
                 self._add_declared(name.asname)
-            elif name.name == "*":
-                raise exceptions.CompileException(
-                    "'import *' is not supported, since all identifier "
-                    "names must be explicitly declared.  Please use the "
-                    "form 'from <modulename> import <name1>, <name2>, "
-                    "...' instead.",
-                    **self.exception_kwargs,
-                )
             else:
+                if name.name == "*":
+                    raise exceptions.CompileException(
+                        "'import *' is not supported, since all identifier "
+                        "names must be explicitly declared.  Please use the "
+                        "form 'from <modulename> import <name1>, <name2>, "
+                        "...' instead.",
+                        **self.exception_kwargs
+                    )
                 self._add_declared(name.name)
 
 
@@ -198,20 +213,27 @@ class ParseFunc(_ast_util.NodeVisitor):
 
         argnames = [arg_id(arg) for arg in node.args.args]
         if node.args.vararg:
-            argnames.append(node.args.vararg.arg)
+            argnames.append(arg_stringname(node.args.vararg))
 
-        kwargnames = [arg_id(arg) for arg in node.args.kwonlyargs]
+        if compat.py2k:
+            # kw-only args don't exist in Python 2
+            kwargnames = []
+        else:
+            kwargnames = [arg_id(arg) for arg in node.args.kwonlyargs]
         if node.args.kwarg:
-            kwargnames.append(node.args.kwarg.arg)
+            kwargnames.append(arg_stringname(node.args.kwarg))
         self.listener.argnames = argnames
         self.listener.defaults = node.args.defaults  # ast
         self.listener.kwargnames = kwargnames
-        self.listener.kwdefaults = node.args.kw_defaults
+        if compat.py2k:
+            self.listener.kwdefaults = []
+        else:
+            self.listener.kwdefaults = node.args.kw_defaults
         self.listener.varargs = node.args.vararg
         self.listener.kwargs = node.args.kwarg
 
 
-class ExpressionGenerator:
+class ExpressionGenerator(object):
     def __init__(self, astnode):
         self.generator = _ast_util.SourceGenerator(" " * 4)
         self.generator.visit(astnode)
diff --git a/third_party/mako/mako/mako/runtime.py b/third_party/mako/mako/mako/runtime.py
index 6d7fa684ac9c..465908e6d844 100644
--- a/third_party/mako/mako/mako/runtime.py
+++ b/third_party/mako/mako/mako/runtime.py
@@ -7,16 +7,16 @@
 """provides runtime services for templates, including Context,
 Namespace, and various helper functions."""
 
-import builtins
 import functools
 import sys
 
 from mako import compat
 from mako import exceptions
 from mako import util
+from mako.compat import compat_builtins
 
 
-class Context:
+class Context(object):
 
     """Provides runtime namespace, output buffer, and various
     callstacks for templates.
@@ -24,7 +24,7 @@ class Context:
     See :ref:`runtime_toplevel` for detail on the usage of
     :class:`.Context`.
 
-    """
+     """
 
     def __init__(self, buffer, **data):
         self._buffer_stack = [buffer]
@@ -103,7 +103,7 @@ class Context:
         if key in self._data:
             return self._data[key]
         else:
-            return builtins.__dict__[key]
+            return compat_builtins.__dict__[key]
 
     def _push_writer(self):
         """push a capturing buffer onto this Context and return
@@ -135,7 +135,7 @@ class Context:
     def get(self, key, default=None):
         """Return a value from this :class:`.Context`."""
 
-        return self._data.get(key, builtins.__dict__.get(key, default))
+        return self._data.get(key, compat_builtins.__dict__.get(key, default))
 
     def write(self, string):
         """Write a string to this :class:`.Context` object's
@@ -216,7 +216,7 @@ class CallerStack(list):
         self.nextcaller = self.pop()
 
 
-class Undefined:
+class Undefined(object):
 
     """Represents an undefined value in a template.
 
@@ -240,7 +240,7 @@ UNDEFINED = Undefined()
 STOP_RENDERING = ""
 
 
-class LoopStack:
+class LoopStack(object):
 
     """a stack for LoopContexts that implements the context manager protocol
     to automatically pop off the top of the stack on context exit
@@ -280,7 +280,7 @@ class LoopStack:
         return iter(self._top)
 
 
-class LoopContext:
+class LoopContext(object):
 
     """A magic loop variable.
     Automatically accessible in any ``% for`` block.
@@ -339,13 +339,14 @@ class LoopContext:
         return bool(self.index % 2)
 
     def cycle(self, *values):
-        """Cycle through values as the loop progresses."""
+        """Cycle through values as the loop progresses.
+        """
         if not values:
             raise ValueError("You must provide values to cycle through")
         return values[self.index % len(values)]
 
 
-class _NSAttr:
+class _NSAttr(object):
     def __init__(self, parent):
         self.__parent = parent
 
@@ -359,22 +360,22 @@ class _NSAttr:
         raise AttributeError(key)
 
 
-class Namespace:
+class Namespace(object):
 
     """Provides access to collections of rendering methods, which
-    can be local, from other templates, or from imported modules.
+      can be local, from other templates, or from imported modules.
 
-    To access a particular rendering method referenced by a
-    :class:`.Namespace`, use plain attribute access:
+      To access a particular rendering method referenced by a
+      :class:`.Namespace`, use plain attribute access:
 
-    .. sourcecode:: mako
+      .. sourcecode:: mako
 
-      ${some_namespace.foo(x, y, z)}
+        ${some_namespace.foo(x, y, z)}
 
-    :class:`.Namespace` also contains several built-in attributes
-    described here.
+      :class:`.Namespace` also contains several built-in attributes
+      described here.
 
-    """
+      """
 
     def __init__(
         self,
@@ -389,7 +390,7 @@ class Namespace:
         self.context = context
         self.inherits = inherits
         if callables is not None:
-            self.callables = {c.__name__: c for c in callables}
+            self.callables = dict([(c.__name__, c) for c in callables])
 
     callables = ()
 
@@ -481,14 +482,15 @@ class Namespace:
         key = (self, uri)
         if key in self.context.namespaces:
             return self.context.namespaces[key]
-        ns = TemplateNamespace(
-            uri,
-            self.context._copy(),
-            templateuri=uri,
-            calling_uri=self._templateuri,
-        )
-        self.context.namespaces[key] = ns
-        return ns
+        else:
+            ns = TemplateNamespace(
+                uri,
+                self.context._copy(),
+                templateuri=uri,
+                calling_uri=self._templateuri,
+            )
+            self.context.namespaces[key] = ns
+            return ns
 
     def get_template(self, uri):
         """Return a :class:`.Template` from the given ``uri``.
@@ -572,7 +574,7 @@ class TemplateNamespace(Namespace):
         self.context = context
         self.inherits = inherits
         if callables is not None:
-            self.callables = {c.__name__: c for c in callables}
+            self.callables = dict([(c.__name__, c) for c in callables])
 
         if templateuri is not None:
             self.template = _lookup_template(context, templateuri, calling_uri)
@@ -664,7 +666,7 @@ class ModuleNamespace(Namespace):
         self.context = context
         self.inherits = inherits
         if callables is not None:
-            self.callables = {c.__name__: c for c in callables}
+            self.callables = dict([(c.__name__, c) for c in callables])
 
         mod = __import__(module)
         for token in module.split(".")[1:]:
@@ -788,7 +790,7 @@ def _include_file(context, uri, calling_uri, **kwargs):
         except Exception:
             result = template.include_error_handler(ctx, compat.exception_as())
             if not result:
-                raise
+                compat.reraise(*sys.exc_info())
     else:
         callable_(ctx, **kwargs)
 
@@ -835,10 +837,8 @@ def _lookup_template(context, uri, relativeto):
     uri = lookup.adjust_uri(uri, relativeto)
     try:
         return lookup.get_template(uri)
-    except exceptions.TopLevelLookupException as e:
-        raise exceptions.TemplateLookupException(
-            str(compat.exception_as())
-        ) from e
+    except exceptions.TopLevelLookupException:
+        raise exceptions.TemplateLookupException(str(compat.exception_as()))
 
 
 def _populate_self_namespace(context, template, self_ns=None):
@@ -862,10 +862,14 @@ def _render(template, callable_, args, data, as_unicode=False):
     output of the given template and template callable."""
 
     if as_unicode:
-        buf = util.FastEncodingBuffer()
+        buf = util.FastEncodingBuffer(as_unicode=True)
+    elif template.bytestring_passthrough:
+        buf = compat.StringIO()
     else:
         buf = util.FastEncodingBuffer(
-            encoding=template.output_encoding, errors=template.encoding_errors
+            as_unicode=as_unicode,
+            encoding=template.output_encoding,
+            errors=template.encoding_errors,
         )
     context = Context(buf, **data)
     context._outputting_as_unicode = as_unicode
@@ -876,7 +880,7 @@ def _render(template, callable_, args, data, as_unicode=False):
         callable_,
         context,
         *args,
-        **_kwargs_for_callable(callable_, data),
+        **_kwargs_for_callable(callable_, data)
     )
     return context._pop_buffer().getvalue()
 
@@ -947,15 +951,13 @@ def _render_error(template, context, error):
     if template.error_handler:
         result = template.error_handler(context, error)
         if not result:
-            tp, value, tb = sys.exc_info()
-            if value and tb:
-                raise value.with_traceback(tb)
-            else:
-                raise error
+            compat.reraise(*sys.exc_info())
     else:
         error_template = exceptions.html_error_template()
         if context._outputting_as_unicode:
-            context._buffer_stack[:] = [util.FastEncodingBuffer()]
+            context._buffer_stack[:] = [
+                util.FastEncodingBuffer(as_unicode=True)
+            ]
         else:
             context._buffer_stack[:] = [
                 util.FastEncodingBuffer(
diff --git a/third_party/mako/mako/mako/template.py b/third_party/mako/mako/mako/template.py
index bbbe73cb2520..5ed23204668f 100644
--- a/third_party/mako/mako/mako/template.py
+++ b/third_party/mako/mako/mako/template.py
@@ -1,5 +1,5 @@
 # mako/template.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -25,7 +25,7 @@ from mako import util
 from mako.lexer import Lexer
 
 
-class Template:
+class Template(object):
 
     r"""Represents a compiled template.
 
@@ -53,6 +53,17 @@ class Template:
      of return-valued ``%def``\ s "opt out" of that filtering via
      passing special attributes or objects.
 
+    :param bytestring_passthrough: When ``True``, and ``output_encoding`` is
+     set to ``None``, and :meth:`.Template.render` is used to render,
+     the `StringIO` or `cStringIO` buffer will be used instead of the
+     default "fast" buffer.   This allows raw bytestrings in the
+     output stream, such as in expressions, to pass straight
+     through to the buffer.  This flag is forced
+     to ``True`` if ``disable_unicode`` is also configured.
+
+     .. versionadded:: 0.4
+        Added to provide the same behavior as that of the previous series.
+
     :param cache_args: Dictionary of cache configuration arguments that
      will be passed to the :class:`.CacheImpl`.   See :ref:`caching_toplevel`.
 
@@ -83,6 +94,9 @@ class Template:
     :param default_filters: List of string filter names that will
      be applied to all expressions.  See :ref:`filtering_default_filters`.
 
+    :param disable_unicode: Disables all awareness of Python Unicode
+     objects.  See :ref:`unicode_disabled`.
+
     :param enable_loop: When ``True``, enable the ``loop`` context variable.
      This can be set to ``False`` to support templates that may
      be making usage of the name "``loop``".   Individual templates can
@@ -241,7 +255,9 @@ class Template:
         cache_url=None,
         module_filename=None,
         input_encoding=None,
+        disable_unicode=False,
         module_writer=None,
+        bytestring_passthrough=False,
         default_filters=None,
         buffer_filters=(),
         strict_undefined=False,
@@ -278,12 +294,26 @@ class Template:
         self.input_encoding = input_encoding
         self.output_encoding = output_encoding
         self.encoding_errors = encoding_errors
+        self.disable_unicode = disable_unicode
+        self.bytestring_passthrough = bytestring_passthrough or disable_unicode
         self.enable_loop = enable_loop
         self.strict_undefined = strict_undefined
         self.module_writer = module_writer
 
+        if compat.py3k and disable_unicode:
+            raise exceptions.UnsupportedError(
+                "Mako for Python 3 does not " "support disabling Unicode"
+            )
+        elif output_encoding and disable_unicode:
+            raise exceptions.UnsupportedError(
+                "output_encoding must be set to "
+                "None when disable_unicode is used."
+            )
         if default_filters is None:
-            self.default_filters = ["str"]
+            if compat.py3k or self.disable_unicode:
+                self.default_filters = ["str"]
+            else:
+                self.default_filters = ["unicode"]
         else:
             self.default_filters = default_filters
         self.buffer_filters = buffer_filters
@@ -357,7 +387,11 @@ class Template:
     ):
         self.cache_impl = cache_impl
         self.cache_enabled = cache_enabled
-        self.cache_args = cache_args or {}
+        if cache_args:
+            self.cache_args = cache_args
+        else:
+            self.cache_args = {}
+
         # transfer deprecated cache_* args
         if cache_type:
             self.cache_args["type"] = cache_type
@@ -429,7 +463,7 @@ class Template:
 
         If the template specifies an output encoding, the string
         will be encoded accordingly, else the output is raw (raw
-        output uses `StringIO` and can't handle multibyte
+        output uses `cStringIO` and can't handle multibyte
         characters). A :class:`.Context` object is created corresponding
         to the given data. Arguments that are explicitly declared
         by this template's internal rendering method are also
@@ -483,17 +517,17 @@ class ModuleTemplate(Template):
 
     """A Template which is constructed given an existing Python module.
 
-    e.g.::
+       e.g.::
 
-         t = Template("this is a template")
-         f = file("mymodule.py", "w")
-         f.write(t.code)
-         f.close()
+            t = Template("this is a template")
+            f = file("mymodule.py", "w")
+            f.write(t.code)
+            f.close()
 
-         import mymodule
+            import mymodule
 
-         t = ModuleTemplate(mymodule)
-         print(t.render())
+            t = ModuleTemplate(mymodule)
+            print(t.render())
 
     """
 
@@ -507,6 +541,8 @@ class ModuleTemplate(Template):
         template_source=None,
         output_encoding=None,
         encoding_errors="strict",
+        disable_unicode=False,
+        bytestring_passthrough=False,
         format_exceptions=False,
         error_handler=None,
         lookup=None,
@@ -523,8 +559,20 @@ class ModuleTemplate(Template):
         self.input_encoding = module._source_encoding
         self.output_encoding = output_encoding
         self.encoding_errors = encoding_errors
+        self.disable_unicode = disable_unicode
+        self.bytestring_passthrough = bytestring_passthrough or disable_unicode
         self.enable_loop = module._enable_loop
 
+        if compat.py3k and disable_unicode:
+            raise exceptions.UnsupportedError(
+                "Mako for Python 3 does not " "support disabling Unicode"
+            )
+        elif output_encoding and disable_unicode:
+            raise exceptions.UnsupportedError(
+                "output_encoding must be set to "
+                "None when disable_unicode is used."
+            )
+
         self.module = module
         self.filename = template_filename
         ModuleInfo(
@@ -568,18 +616,19 @@ class DefTemplate(Template):
         self.include_error_handler = parent.include_error_handler
         self.enable_loop = parent.enable_loop
         self.lookup = parent.lookup
+        self.bytestring_passthrough = parent.bytestring_passthrough
 
     def get_def(self, name):
         return self.parent.get_def(name)
 
 
-class ModuleInfo:
+class ModuleInfo(object):
 
     """Stores information about a module currently loaded into
     memory, provides reverse lookups of template source, module
     source code based on a module's identifier.
 
-    """
+     """
 
     _modules = weakref.WeakValueDictionary()
 
@@ -609,9 +658,9 @@ class ModuleInfo:
             r"__M_BEGIN_METADATA(.+?)__M_END_METADATA", module_source, re.S
         ).group(1)
         source_map = json.loads(source_map)
-        source_map["line_map"] = {
-            int(k): int(v) for k, v in source_map["line_map"].items()
-        }
+        source_map["line_map"] = dict(
+            (int(k), int(v)) for k, v in source_map["line_map"].items()
+        )
         if full_line_map:
             f_line_map = source_map["full_line_map"] = []
             line_map = source_map["line_map"]
@@ -632,25 +681,28 @@ class ModuleInfo:
 
     @property
     def source(self):
-        if self.template_source is None:
+        if self.template_source is not None:
+            if self.module._source_encoding and not isinstance(
+                self.template_source, compat.text_type
+            ):
+                return self.template_source.decode(
+                    self.module._source_encoding
+                )
+            else:
+                return self.template_source
+        else:
             data = util.read_file(self.template_filename)
             if self.module._source_encoding:
                 return data.decode(self.module._source_encoding)
             else:
                 return data
 
-        elif self.module._source_encoding and not isinstance(
-            self.template_source, str
-        ):
-            return self.template_source.decode(self.module._source_encoding)
-        else:
-            return self.template_source
-
 
 def _compile(template, text, filename, generate_magic_comment):
     lexer = template.lexer_cls(
         text,
         filename,
+        disable_unicode=template.disable_unicode,
         input_encoding=template.input_encoding,
         preprocessor=template.preprocessor,
     )
@@ -665,6 +717,7 @@ def _compile(template, text, filename, generate_magic_comment):
         future_imports=template.future_imports,
         source_encoding=lexer.encoding,
         generate_magic_comment=generate_magic_comment,
+        disable_unicode=template.disable_unicode,
         strict_undefined=template.strict_undefined,
         enable_loop=template.enable_loop,
         reserved_names=template.reserved_names,
@@ -675,10 +728,15 @@ def _compile(template, text, filename, generate_magic_comment):
 def _compile_text(template, text, filename):
     identifier = template.module_id
     source, lexer = _compile(
-        template, text, filename, generate_magic_comment=False
+        template,
+        text,
+        filename,
+        generate_magic_comment=template.disable_unicode,
     )
 
     cid = identifier
+    if not compat.py3k and isinstance(cid, compat.text_type):
+        cid = cid.encode()
     module = types.ModuleType(cid)
     code = compile(source, cid, "exec")
 
@@ -692,7 +750,7 @@ def _compile_module_file(template, text, filename, outputpath, module_writer):
         template, text, filename, generate_magic_comment=True
     )
 
-    if isinstance(source, str):
+    if isinstance(source, compat.text_type):
         source = source.encode(lexer.encoding or "ascii")
 
     if module_writer:
@@ -709,7 +767,10 @@ def _compile_module_file(template, text, filename, outputpath, module_writer):
 
 
 def _get_module_info_from_callable(callable_):
-    return _get_module_info(callable_.__globals__["__name__"])
+    if compat.py3k:
+        return _get_module_info(callable_.__globals__["__name__"])
+    else:
+        return _get_module_info(callable_.func_globals["__name__"])
 
 
 def _get_module_info(filename):
diff --git a/third_party/mako/mako/mako/testing/__init__.py b/third_party/mako/mako/mako/testing/__init__.py
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/third_party/mako/mako/mako/testing/_config.py b/third_party/mako/mako/mako/testing/_config.py
deleted file mode 100644
index 4ee3d0a6ed77..000000000000
--- a/third_party/mako/mako/mako/testing/_config.py
+++ /dev/null
@@ -1,128 +0,0 @@
-import configparser
-import dataclasses
-from dataclasses import dataclass
-from pathlib import Path
-from typing import Callable
-from typing import ClassVar
-from typing import Optional
-from typing import Union
-
-from .helpers import make_path
-
-
-class ConfigError(BaseException):
-    pass
-
-
-class MissingConfig(ConfigError):
-    pass
-
-
-class MissingConfigSection(ConfigError):
-    pass
-
-
-class MissingConfigItem(ConfigError):
-    pass
-
-
-class ConfigValueTypeError(ConfigError):
-    pass
-
-
-class _GetterDispatch:
-    def __init__(self, initialdata, default_getter: Callable):
-        self.default_getter = default_getter
-        self.data = initialdata
-
-    def get_fn_for_type(self, type_):
-        return self.data.get(type_, self.default_getter)
-
-    def get_typed_value(self, type_, name):
-        get_fn = self.get_fn_for_type(type_)
-        return get_fn(name)
-
-
-def _parse_cfg_file(filespec: Union[Path, str]):
-    cfg = configparser.ConfigParser()
-    try:
-        filepath = make_path(filespec, check_exists=True)
-    except FileNotFoundError as e:
-        raise MissingConfig(f"No config file found at {filespec}") from e
-    else:
-        with open(filepath, encoding="utf-8") as f:
-            cfg.read_file(f)
-        return cfg
-
-
-def _build_getter(cfg_obj, cfg_section, method, converter=None):
-    def caller(option, **kwargs):
-        try:
-            rv = getattr(cfg_obj, method)(cfg_section, option, **kwargs)
-        except configparser.NoSectionError as nse:
-            raise MissingConfigSection(
-                f"No config section named {cfg_section}"
-            ) from nse
-        except configparser.NoOptionError as noe:
-            raise MissingConfigItem(f"No config item for {option}") from noe
-        except ValueError as ve:
-            # ConfigParser.getboolean, .getint, .getfloat raise ValueError
-            # on bad types
-            raise ConfigValueTypeError(
-                f"Wrong value type for {option}"
-            ) from ve
-        else:
-            if converter:
-                try:
-                    rv = converter(rv)
-                except Exception as e:
-                    raise ConfigValueTypeError(
-                        f"Wrong value type for {option}"
-                    ) from e
-            return rv
-
-    return caller
-
-
-def _build_getter_dispatch(cfg_obj, cfg_section, converters=None):
-    converters = converters or {}
-
-    default_getter = _build_getter(cfg_obj, cfg_section, "get")
-
-    # support ConfigParser builtins
-    getters = {
-        int: _build_getter(cfg_obj, cfg_section, "getint"),
-        bool: _build_getter(cfg_obj, cfg_section, "getboolean"),
-        float: _build_getter(cfg_obj, cfg_section, "getfloat"),
-        str: default_getter,
-    }
-
-    # use ConfigParser.get and convert value
-    getters.update(
-        {
-            type_: _build_getter(
-                cfg_obj, cfg_section, "get", converter=converter_fn
-            )
-            for type_, converter_fn in converters.items()
-        }
-    )
-
-    return _GetterDispatch(getters, default_getter)
-
-
-@dataclass
-class ReadsCfg:
-    section_header: ClassVar[str]
-    converters: ClassVar[Optional[dict]] = None
-
-    @classmethod
-    def from_cfg_file(cls, filespec: Union[Path, str]):
-        cfg = _parse_cfg_file(filespec)
-        dispatch = _build_getter_dispatch(
-            cfg, cls.section_header, converters=cls.converters
-        )
-        kwargs = {
-            field.name: dispatch.get_typed_value(field.type, field.name)
-            for field in dataclasses.fields(cls)
-        }
-        return cls(**kwargs)
diff --git a/third_party/mako/mako/mako/testing/assertions.py b/third_party/mako/mako/mako/testing/assertions.py
deleted file mode 100644
index 14ea63523ff3..000000000000
--- a/third_party/mako/mako/mako/testing/assertions.py
+++ /dev/null
@@ -1,167 +0,0 @@
-import contextlib
-import re
-import sys
-
-
-def eq_(a, b, msg=None):
-    """Assert a == b, with repr messaging on failure."""
-    assert a == b, msg or "%r != %r" % (a, b)
-
-
-def ne_(a, b, msg=None):
-    """Assert a != b, with repr messaging on failure."""
-    assert a != b, msg or "%r == %r" % (a, b)
-
-
-def in_(a, b, msg=None):
-    """Assert a in b, with repr messaging on failure."""
-    assert a in b, msg or "%r not in %r" % (a, b)
-
-
-def not_in(a, b, msg=None):
-    """Assert a in not b, with repr messaging on failure."""
-    assert a not in b, msg or "%r is in %r" % (a, b)
-
-
-def _assert_proper_exception_context(exception):
-    """assert that any exception we're catching does not have a __context__
-    without a __cause__, and that __suppress_context__ is never set.
-
-    Python 3 will report nested as exceptions as "during the handling of
-    error X, error Y occurred". That's not what we want to do. We want
-    these exceptions in a cause chain.
-
-    """
-
-    if (
-        exception.__context__ is not exception.__cause__
-        and not exception.__suppress_context__
-    ):
-        assert False, (
-            "Exception %r was correctly raised but did not set a cause, "
-            "within context %r as its cause."
-            % (exception, exception.__context__)
-        )
-
-
-def _assert_proper_cause_cls(exception, cause_cls):
-    """assert that any exception we're catching does not have a __context__
-    without a __cause__, and that __suppress_context__ is never set.
-
-    Python 3 will report nested as exceptions as "during the handling of
-    error X, error Y occurred". That's not what we want to do. We want
-    these exceptions in a cause chain.
-
-    """
-    assert isinstance(exception.__cause__, cause_cls), (
-        "Exception %r was correctly raised but has cause %r, which does not "
-        "have the expected cause type %r."
-        % (exception, exception.__cause__, cause_cls)
-    )
-
-
-def assert_raises(except_cls, callable_, *args, **kw):
-    return _assert_raises(except_cls, callable_, args, kw)
-
-
-def assert_raises_with_proper_context(except_cls, callable_, *args, **kw):
-    return _assert_raises(except_cls, callable_, args, kw, check_context=True)
-
-
-def assert_raises_with_given_cause(
-    except_cls, cause_cls, callable_, *args, **kw
-):
-    return _assert_raises(except_cls, callable_, args, kw, cause_cls=cause_cls)
-
-
-def assert_raises_message(except_cls, msg, callable_, *args, **kwargs):
-    return _assert_raises(except_cls, callable_, args, kwargs, msg=msg)
-
-
-def assert_raises_message_with_proper_context(
-    except_cls, msg, callable_, *args, **kwargs
-):
-    return _assert_raises(
-        except_cls, callable_, args, kwargs, msg=msg, check_context=True
-    )
-
-
-def assert_raises_message_with_given_cause(
-    except_cls, msg, cause_cls, callable_, *args, **kwargs
-):
-    return _assert_raises(
-        except_cls, callable_, args, kwargs, msg=msg, cause_cls=cause_cls
-    )
-
-
-def _assert_raises(
-    except_cls,
-    callable_,
-    args,
-    kwargs,
-    msg=None,
-    check_context=False,
-    cause_cls=None,
-):
-
-    with _expect_raises(except_cls, msg, check_context, cause_cls) as ec:
-        callable_(*args, **kwargs)
-    return ec.error
-
-
-class _ErrorContainer:
-    error = None
-
-
-@contextlib.contextmanager
-def _expect_raises(except_cls, msg=None, check_context=False, cause_cls=None):
-    ec = _ErrorContainer()
-    if check_context:
-        are_we_already_in_a_traceback = sys.exc_info()[0]
-    try:
-        yield ec
-        success = False
-    except except_cls as err:
-        ec.error = err
-        success = True
-        if msg is not None:
-            # I'm often pdbing here, and "err" above isn't
-            # in scope, so assign the string explicitly
-            error_as_string = str(err)
-            assert re.search(msg, error_as_string, re.UNICODE), "%r !~ %s" % (
-                msg,
-                error_as_string,
-            )
-        if cause_cls is not None:
-            _assert_proper_cause_cls(err, cause_cls)
-        if check_context and not are_we_already_in_a_traceback:
-            _assert_proper_exception_context(err)
-        print(str(err).encode("utf-8"))
-
-    # it's generally a good idea to not carry traceback objects outside
-    # of the except: block, but in this case especially we seem to have
-    # hit some bug in either python 3.10.0b2 or greenlet or both which
-    # this seems to fix:
-    # https://github.com/python-greenlet/greenlet/issues/242
-    del ec
-
-    # assert outside the block so it works for AssertionError too !
-    assert success, "Callable did not raise an exception"
-
-
-def expect_raises(except_cls, check_context=False):
-    return _expect_raises(except_cls, check_context=check_context)
-
-
-def expect_raises_message(except_cls, msg, check_context=False):
-    return _expect_raises(except_cls, msg=msg, check_context=check_context)
-
-
-def expect_raises_with_proper_context(except_cls, check_context=True):
-    return _expect_raises(except_cls, check_context=check_context)
-
-
-def expect_raises_message_with_proper_context(
-    except_cls, msg, check_context=True
-):
-    return _expect_raises(except_cls, msg=msg, check_context=check_context)
diff --git a/third_party/mako/mako/mako/testing/config.py b/third_party/mako/mako/mako/testing/config.py
deleted file mode 100644
index b77d0c08084b..000000000000
--- a/third_party/mako/mako/mako/testing/config.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from dataclasses import dataclass
-from pathlib import Path
-
-from ._config import ReadsCfg
-from .helpers import make_path
-
-
-@dataclass
-class Config(ReadsCfg):
-    module_base: Path
-    template_base: Path
-
-    section_header = "mako_testing"
-    converters = {Path: make_path}
-
-
-config = Config.from_cfg_file("./setup.cfg")
diff --git a/third_party/mako/mako/mako/testing/exclusions.py b/third_party/mako/mako/mako/testing/exclusions.py
deleted file mode 100644
index 37b2d14a395f..000000000000
--- a/third_party/mako/mako/mako/testing/exclusions.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import pytest
-
-from mako.ext.beaker_cache import has_beaker
-from mako.util import update_wrapper
-
-
-try:
-    import babel.messages.extract as babel
-except ImportError:
-    babel = None
-
-
-try:
-    import lingua
-except ImportError:
-    lingua = None
-
-
-try:
-    import dogpile.cache  # noqa
-except ImportError:
-    has_dogpile_cache = False
-else:
-    has_dogpile_cache = True
-
-
-requires_beaker = pytest.mark.skipif(
-    not has_beaker, reason="Beaker is required for these tests."
-)
-
-
-requires_babel = pytest.mark.skipif(
-    babel is None, reason="babel not installed: skipping babelplugin test"
-)
-
-
-requires_lingua = pytest.mark.skipif(
-    lingua is None, reason="lingua not installed: skipping linguaplugin test"
-)
-
-
-requires_dogpile_cache = pytest.mark.skipif(
-    not has_dogpile_cache,
-    reason="dogpile.cache is required to run these tests",
-)
-
-
-def _pygments_version():
-    try:
-        import pygments
-
-        version = pygments.__version__
-    except:
-        version = "0"
-    return version
-
-
-requires_pygments_14 = pytest.mark.skipif(
-    _pygments_version() < "1.4", reason="Requires pygments 1.4 or greater"
-)
-
-
-# def requires_pygments_14(fn):
-
-#     return skip_if(
-#         lambda: version < "1.4", "Requires pygments 1.4 or greater"
-#     )(fn)
-
-
-def requires_no_pygments_exceptions(fn):
-    def go(*arg, **kw):
-        from mako import exceptions
-
-        exceptions._install_fallback()
-        try:
-            return fn(*arg, **kw)
-        finally:
-            exceptions._install_highlighting()
-
-    return update_wrapper(go, fn)
diff --git a/third_party/mako/mako/mako/testing/fixtures.py b/third_party/mako/mako/mako/testing/fixtures.py
deleted file mode 100644
index c9379c0c7f25..000000000000
--- a/third_party/mako/mako/mako/testing/fixtures.py
+++ /dev/null
@@ -1,109 +0,0 @@
-import os
-
-from mako.cache import CacheImpl
-from mako.cache import register_plugin
-from mako.template import Template
-from .assertions import eq_
-from .config import config
-
-
-class TemplateTest:
-    def _file_template(self, filename, **kw):
-        filepath = self._file_path(filename)
-        return Template(
-            uri=filename,
-            filename=filepath,
-            module_directory=config.module_base,
-            **kw,
-        )
-
-    def _file_path(self, filename):
-        name, ext = os.path.splitext(filename)
-        py3k_path = os.path.join(config.template_base, name + "_py3k" + ext)
-        if os.path.exists(py3k_path):
-            return py3k_path
-
-        return os.path.join(config.template_base, filename)
-
-    def _do_file_test(
-        self,
-        filename,
-        expected,
-        filters=None,
-        unicode_=True,
-        template_args=None,
-        **kw,
-    ):
-        t1 = self._file_template(filename, **kw)
-        self._do_test(
-            t1,
-            expected,
-            filters=filters,
-            unicode_=unicode_,
-            template_args=template_args,
-        )
-
-    def _do_memory_test(
-        self,
-        source,
-        expected,
-        filters=None,
-        unicode_=True,
-        template_args=None,
-        **kw,
-    ):
-        t1 = Template(text=source, **kw)
-        self._do_test(
-            t1,
-            expected,
-            filters=filters,
-            unicode_=unicode_,
-            template_args=template_args,
-        )
-
-    def _do_test(
-        self,
-        template,
-        expected,
-        filters=None,
-        template_args=None,
-        unicode_=True,
-    ):
-        if template_args is None:
-            template_args = {}
-        if unicode_:
-            output = template.render_unicode(**template_args)
-        else:
-            output = template.render(**template_args)
-
-        if filters:
-            output = filters(output)
-        eq_(output, expected)
-
-
-class PlainCacheImpl(CacheImpl):
-    """Simple memory cache impl so that tests which
-    use caching can run without beaker."""
-
-    def __init__(self, cache):
-        self.cache = cache
-        self.data = {}
-
-    def get_or_create(self, key, creation_function, **kw):
-        if key in self.data:
-            return self.data[key]
-        else:
-            self.data[key] = data = creation_function(**kw)
-            return data
-
-    def put(self, key, value, **kw):
-        self.data[key] = value
-
-    def get(self, key, **kw):
-        return self.data[key]
-
-    def invalidate(self, key, **kw):
-        del self.data[key]
-
-
-register_plugin("plain", __name__, "PlainCacheImpl")
diff --git a/third_party/mako/mako/mako/testing/helpers.py b/third_party/mako/mako/mako/testing/helpers.py
deleted file mode 100644
index 77cca3672516..000000000000
--- a/third_party/mako/mako/mako/testing/helpers.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import contextlib
-import pathlib
-from pathlib import Path
-import re
-import time
-from typing import Union
-from unittest import mock
-
-
-def flatten_result(result):
-    return re.sub(r"[\s\r\n]+", " ", result).strip()
-
-
-def result_lines(result):
-    return [
-        x.strip()
-        for x in re.split(r"\r?\n", re.sub(r" +", " ", result))
-        if x.strip() != ""
-    ]
-
-
-def make_path(
-    filespec: Union[Path, str],
-    make_absolute: bool = True,
-    check_exists: bool = False,
-) -> Path:
-    path = Path(filespec)
-    if make_absolute:
-        path = path.resolve(strict=check_exists)
-    if check_exists and (not path.exists()):
-        raise FileNotFoundError(f"No file or directory at {filespec}")
-    return path
-
-
-def _unlink_path(path, missing_ok=False):
-    # Replicate 3.8+ functionality in 3.7
-    cm = contextlib.nullcontext()
-    if missing_ok:
-        cm = contextlib.suppress(FileNotFoundError)
-
-    with cm:
-        path.unlink()
-
-
-def replace_file_with_dir(pathspec):
-    path = pathlib.Path(pathspec)
-    _unlink_path(path, missing_ok=True)
-    path.mkdir(exist_ok=True)
-    return path
-
-
-def file_with_template_code(filespec):
-    with open(filespec, "w") as f:
-        f.write(
-            """
-i am an artificial template just for you
-"""
-        )
-    return filespec
-
-
-@contextlib.contextmanager
-def rewind_compile_time(hours=1):
-    rewound = time.time() - (hours * 3_600)
-    with mock.patch("mako.codegen.time") as codegen_time:
-        codegen_time.time.return_value = rewound
-        yield
diff --git a/third_party/mako/mako/mako/util.py b/third_party/mako/mako/mako/util.py
index 74c8b9eb25fa..16e3c72658c4 100644
--- a/third_party/mako/mako/mako/util.py
+++ b/third_party/mako/mako/mako/util.py
@@ -1,8 +1,10 @@
 # mako/util.py
-# Copyright 2006-2021 the Mako authors and contributors <see AUTHORS file>
+# Copyright 2006-2020 the Mako authors and contributors <see AUTHORS file>
 #
 # This module is part of Mako and is released under
 # the MIT License: http://www.opensource.org/licenses/mit-license.php
+from __future__ import absolute_import
+
 from ast import parse
 import codecs
 import collections
@@ -11,7 +13,7 @@ import os
 import re
 import timeit
 
-from .compat import importlib_metadata_get
+from mako import compat
 
 
 def update_wrapper(decorated, fn):
@@ -20,7 +22,7 @@ def update_wrapper(decorated, fn):
     return decorated
 
 
-class PluginLoader:
+class PluginLoader(object):
     def __init__(self, group):
         self.group = group
         self.impls = {}
@@ -28,17 +30,18 @@ class PluginLoader:
     def load(self, name):
         if name in self.impls:
             return self.impls[name]()
+        else:
+            import pkg_resources
 
-        for impl in importlib_metadata_get(self.group):
-            if impl.name == name:
+            for impl in pkg_resources.iter_entry_points(self.group, name):
                 self.impls[name] = impl.load
                 return impl.load()
+            else:
+                from mako import exceptions
 
-        from mako import exceptions
-
-        raise exceptions.RuntimeException(
-            "Can't load plugin %s %s" % (self.group, name)
-        )
+                raise exceptions.RuntimeException(
+                    "Can't load plugin %s %s" % (self.group, name)
+                )
 
     def register(self, name, modulepath, objname):
         def load():
@@ -58,7 +61,7 @@ def verify_directory(dir_):
     while not os.path.exists(dir_):
         try:
             tries += 1
-            os.makedirs(dir_, 0o755)
+            os.makedirs(dir_, compat.octal("0775"))
         except:
             if tries > 5:
                 raise
@@ -73,7 +76,7 @@ def to_list(x, default=None):
         return x
 
 
-class memoized_property:
+class memoized_property(object):
 
     """A read-only @property that is only evaluated once."""
 
@@ -89,7 +92,7 @@ class memoized_property:
         return result
 
 
-class memoized_instancemethod:
+class memoized_instancemethod(object):
 
     """Decorate a method memoize its return value.
 
@@ -137,15 +140,19 @@ class SetLikeDict(dict):
         return x
 
 
-class FastEncodingBuffer:
+class FastEncodingBuffer(object):
 
     """a very rudimentary buffer that is faster than StringIO,
-    and supports unicode data."""
+    but doesn't crash on unicode data like cStringIO."""
 
-    def __init__(self, encoding=None, errors="strict"):
+    def __init__(self, encoding=None, errors="strict", as_unicode=False):
         self.data = collections.deque()
         self.encoding = encoding
-        self.delim = ""
+        if as_unicode:
+            self.delim = compat.u("")
+        else:
+            self.delim = ""
+        self.as_unicode = as_unicode
         self.errors = errors
         self.write = self.data.append
 
@@ -172,7 +179,7 @@ class LRUCache(dict):
     is inexact.
     """
 
-    class _Item:
+    class _Item(object):
         def __init__(self, key, value):
             self.key = key
             self.value = value
@@ -196,8 +203,9 @@ class LRUCache(dict):
     def setdefault(self, key, value):
         if key in self:
             return self[key]
-        self[key] = value
-        return value
+        else:
+            self[key] = value
+            return value
 
     def __setitem__(self, key, value):
         item = dict.get(self, key)
@@ -287,7 +295,7 @@ def sorted_dict_repr(d):
     """
     keys = list(d.keys())
     keys.sort()
-    return "{" + ", ".join("%r: %r" % (k, d[k]) for k in keys) + "}"
+    return "{" + ", ".join(["%r: %r" % (k, d[k]) for k in keys]) + "}"
 
 
 def restore__ast(_ast):
@@ -300,7 +308,7 @@ def restore__ast(_ast):
     m = compile(
         """\
 def foo(): pass
-class Bar: pass
+class Bar(object): pass
 if False: pass
 baz = 'mako'
 1 + 2 - 3 * 4 / 5
@@ -372,8 +380,12 @@ mako in baz not in mako""",
 
 
 def read_file(path, mode="rb"):
-    with open(path, mode) as fp:
-        return fp.read()
+    fp = open(path, mode)
+    try:
+        data = fp.read()
+        return data
+    finally:
+        fp.close()
 
 
 def read_python_file(path):
diff --git a/third_party/mako/mako/pyproject.toml b/third_party/mako/mako/pyproject.toml
deleted file mode 100644
index b3193b916778..000000000000
--- a/third_party/mako/mako/pyproject.toml
+++ /dev/null
@@ -1,7 +0,0 @@
-[build-system]
-build-backend = 'setuptools.build_meta'
-requires = ['setuptools >= 47', 'wheel']
-
-[tool.black]
-line-length = 79
-target-version = ['py37']
diff --git a/third_party/mako/mako/setup.cfg b/third_party/mako/mako/setup.cfg
index b7272dbaaa4e..5ec681bb7cd6 100644
--- a/third_party/mako/mako/setup.cfg
+++ b/third_party/mako/mako/setup.cfg
@@ -1,69 +1,3 @@
-[metadata]
-name = Mako
-version = attr: mako.__version__
-description = A super-fast templating language that borrows the best ideas from the existing templating languages.
-long_description = file: README.rst
-long_description_content_type = text/x-rst
-url = https://www.makotemplates.org/
-author = Mike Bayer
-author_email = mike@zzzcomputing.com
-license = MIT
-license_file = LICENSE
-classifiers = 
-	Development Status :: 5 - Production/Stable
-	License :: OSI Approved :: MIT License
-	Environment :: Web Environment
-	Intended Audience :: Developers
-	Programming Language :: Python
-	Programming Language :: Python :: 3
-	Programming Language :: Python :: 3.7
-	Programming Language :: Python :: 3.8
-	Programming Language :: Python :: 3.9
-	Programming Language :: Python :: 3.10
-	Programming Language :: Python :: Implementation :: CPython
-	Programming Language :: Python :: Implementation :: PyPy
-	Topic :: Internet :: WWW/HTTP :: Dynamic Content
-project_urls = 
-	Documentation=https://docs.makotemplates.org
-	Issue Tracker=https://github.com/sqlalchemy/mako
-
-[options]
-packages = find:
-python_requires = >=3.7
-zip_safe = false
-install_requires = 
-	MarkupSafe >= 0.9.2
-	importlib-metadata;python_version<"3.8"
-
-[options.packages.find]
-exclude = 
-	test*
-	examples*
-
-[options.extras_require]
-testing = 
-	pytest
-babel = 
-	Babel
-lingua = 
-	lingua
-
-[options.entry_points]
-python.templating.engines = 
-	mako = mako.ext.turbogears:TGPlugin
-pygments.lexers = 
-	mako = mako.ext.pygmentplugin:MakoLexer
-	html+mako = mako.ext.pygmentplugin:MakoHtmlLexer
-	xml+mako = mako.ext.pygmentplugin:MakoXmlLexer
-	js+mako = mako.ext.pygmentplugin:MakoJavascriptLexer
-	css+mako = mako.ext.pygmentplugin:MakoCssLexer
-babel.extractors = 
-	mako = mako.ext.babelplugin:extract [babel]
-lingua.extractors = 
-	mako = mako.ext.linguaplugin:LinguaMakoExtractor [lingua]
-console_scripts = 
-	mako-render = mako.cmd:cmdline
-
 [egg_info]
 tag_build = 
 tag_date = 0
@@ -71,11 +5,10 @@ tag_date = 0
 [tool:pytest]
 addopts = --tb native -v -r fxX -W error
 python_files = test/*test_*.py
-python_classes = *Test
 
 [upload]
 sign = 1
-identity = 4BFDF51E
+identity = C4DAFEE1
 
 [flake8]
 show-source = true
@@ -91,7 +24,6 @@ exclude = .venv,.git,.tox,dist,docs/*,*egg,build
 import-order-style = google
 application-import-names = mako,test
 
-[mako_testing]
-module_base = ./test/templates/modules
-template_base = ./test/templates/
+[bdist_wheel]
+universal = 1
 
diff --git a/third_party/mako/mako/setup.py b/third_party/mako/mako/setup.py
index 606849326a40..cc5127dfddf5 100644
--- a/third_party/mako/mako/setup.py
+++ b/third_party/mako/mako/setup.py
@@ -1,3 +1,99 @@
+import os
+import re
+import sys
+
+from setuptools import find_packages
 from setuptools import setup
+from setuptools.command.test import test as TestCommand
+
+v = open(os.path.join(os.path.dirname(__file__), "mako", "__init__.py"))
+VERSION = (
+    re.compile(r".*__version__ = [\"'](.*?)[\"']", re.S)
+    .match(v.read())
+    .group(1)
+)
+v.close()
+
+readme = os.path.join(os.path.dirname(__file__), "README.rst")
+
+install_requires = ["MarkupSafe>=0.9.2"]
+
+
+class UseTox(TestCommand):
+    RED = 31
+    RESET_SEQ = "\033[0m"
+    BOLD_SEQ = "\033[1m"
+    COLOR_SEQ = "\033[1;%dm"
+
+    def run_tests(self):
+        sys.stderr.write(
+            "%s%spython setup.py test is deprecated by PyPA.  Please invoke "
+            "'tox' with no arguments for a basic test run.\n%s"
+            % (self.COLOR_SEQ % self.RED, self.BOLD_SEQ, self.RESET_SEQ)
+        )
+        sys.exit(1)
+
+
+setup(
+    name="Mako",
+    version=VERSION,
+    description="A super-fast templating language that borrows the \
+ best ideas from the existing templating languages.",
+    long_description=open(readme).read(),
+    python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
+    classifiers=[
+        "Development Status :: 5 - Production/Stable",
+        "License :: OSI Approved :: MIT License",
+        "Environment :: Web Environment",
+        "Intended Audience :: Developers",
+        "Programming Language :: Python",
+        "Programming Language :: Python :: 3",
+        "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: Implementation :: CPython",
+        "Programming Language :: Python :: Implementation :: PyPy",
+        "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
+    ],
+    keywords="templates",
+    author="Mike Bayer",
+    author_email="mike@zzzcomputing.com",
+    url="https://www.makotemplates.org/",
+    project_urls={
+        "Documentation": "https://docs.makotemplates.org",
+        "Issue Tracker": "https://github.com/sqlalchemy/mako",
+    },
+    license="MIT",
+    packages=find_packages(".", exclude=["examples*", "test*"]),
+    cmdclass={"test": UseTox},
+    zip_safe=False,
+    install_requires=install_requires,
+    entry_points="""
+      [python.templating.engines]
+      mako = mako.ext.turbogears:TGPlugin
+
+      [pygments.lexers]
+      mako = mako.ext.pygmentplugin:MakoLexer
+      html+mako = mako.ext.pygmentplugin:MakoHtmlLexer
+      xml+mako = mako.ext.pygmentplugin:MakoXmlLexer
+      js+mako = mako.ext.pygmentplugin:MakoJavascriptLexer
+      css+mako = mako.ext.pygmentplugin:MakoCssLexer
+
+      [babel.extractors]
+      mako = mako.ext.babelplugin:extract [babel]
+
+      [lingua.extractors]
+      mako = mako.ext.linguaplugin:LinguaMakoExtractor [lingua]
 
-setup()
+      [console_scripts]
+      mako-render = mako.cmd:cmdline
+      """,
+    extras_require={
+        'babel': [
+            'Babel',
+        ],
+        'lingua': [
+            'lingua',
+        ],
+    },
+)
diff --git a/third_party/mako/mako/test/__init__.py b/third_party/mako/mako/test/__init__.py
index e69de29bb2d1..177096201e45 100644
--- a/third_party/mako/mako/test/__init__.py
+++ b/third_party/mako/mako/test/__init__.py
@@ -0,0 +1,211 @@
+import contextlib
+import os
+import re
+import unittest
+
+from mako import compat
+from mako.cache import CacheImpl
+from mako.cache import register_plugin
+from mako.compat import py3k
+from mako.template import Template
+from mako.util import update_wrapper
+
+template_base = os.path.join(os.path.dirname(__file__), "templates")
+module_base = os.path.join(template_base, "modules")
+
+
+class TemplateTest(unittest.TestCase):
+    def _file_template(self, filename, **kw):
+        filepath = self._file_path(filename)
+        return Template(
+            uri=filename, filename=filepath, module_directory=module_base, **kw
+        )
+
+    def _file_path(self, filename):
+        name, ext = os.path.splitext(filename)
+
+        if py3k:
+            py3k_path = os.path.join(template_base, name + "_py3k" + ext)
+            if os.path.exists(py3k_path):
+                return py3k_path
+
+        return os.path.join(template_base, filename)
+
+    def _do_file_test(
+        self,
+        filename,
+        expected,
+        filters=None,
+        unicode_=True,
+        template_args=None,
+        **kw
+    ):
+        t1 = self._file_template(filename, **kw)
+        self._do_test(
+            t1,
+            expected,
+            filters=filters,
+            unicode_=unicode_,
+            template_args=template_args,
+        )
+
+    def _do_memory_test(
+        self,
+        source,
+        expected,
+        filters=None,
+        unicode_=True,
+        template_args=None,
+        **kw
+    ):
+        t1 = Template(text=source, **kw)
+        self._do_test(
+            t1,
+            expected,
+            filters=filters,
+            unicode_=unicode_,
+            template_args=template_args,
+        )
+
+    def _do_test(
+        self,
+        template,
+        expected,
+        filters=None,
+        template_args=None,
+        unicode_=True,
+    ):
+        if template_args is None:
+            template_args = {}
+        if unicode_:
+            output = template.render_unicode(**template_args)
+        else:
+            output = template.render(**template_args)
+
+        if filters:
+            output = filters(output)
+        eq_(output, expected)
+
+
+def eq_(a, b, msg=None):
+    """Assert a == b, with repr messaging on failure."""
+    assert a == b, msg or "%r != %r" % (a, b)
+
+
+def teardown():
+    import shutil
+
+    shutil.rmtree(module_base, True)
+
+
+if py3k:
+    from unittest import mock  # noqa
+else:
+    import mock  # noqa
+
+
+@contextlib.contextmanager
+def raises(except_cls, message=None):
+    try:
+        yield
+        success = False
+    except except_cls as e:
+        if message:
+            assert re.search(
+                message, compat.text_type(e), re.UNICODE
+            ), "%r !~ %s" % (message, e)
+            print(compat.text_type(e).encode("utf-8"))
+        success = True
+
+    # assert outside the block so it works for AssertionError too !
+    assert success, "Callable did not raise an exception"
+
+
+def assert_raises(except_cls, callable_, *args, **kw):
+    with raises(except_cls):
+        return callable_(*args, **kw)
+
+
+def assert_raises_message(except_cls, msg, callable_, *args, **kwargs):
+    with raises(except_cls, msg):
+        return callable_(*args, **kwargs)
+
+
+def skip_if(predicate, reason=None):
+    """Skip a test if predicate is true."""
+    reason = reason or predicate.__name__
+
+    def decorate(fn):
+        fn_name = fn.__name__
+
+        def maybe(*args, **kw):
+            if predicate():
+                msg = "'%s' skipped: %s" % (fn_name, reason)
+                raise unittest.SkipTest(msg)
+            else:
+                return fn(*args, **kw)
+
+        return update_wrapper(maybe, fn)
+
+    return decorate
+
+
+def requires_python_3(fn):
+    return skip_if(lambda: not py3k, "Requires Python 3.xx")(fn)
+
+
+def requires_python_2(fn):
+    return skip_if(lambda: py3k, "Requires Python 2.xx")(fn)
+
+
+def requires_pygments_14(fn):
+    try:
+        import pygments
+
+        version = pygments.__version__
+    except:
+        version = "0"
+    return skip_if(
+        lambda: version < "1.4", "Requires pygments 1.4 or greater"
+    )(fn)
+
+
+def requires_no_pygments_exceptions(fn):
+    def go(*arg, **kw):
+        from mako import exceptions
+
+        exceptions._install_fallback()
+        try:
+            return fn(*arg, **kw)
+        finally:
+            exceptions._install_highlighting()
+
+    return update_wrapper(go, fn)
+
+
+class PlainCacheImpl(CacheImpl):
+    """Simple memory cache impl so that tests which
+    use caching can run without beaker.  """
+
+    def __init__(self, cache):
+        self.cache = cache
+        self.data = {}
+
+    def get_or_create(self, key, creation_function, **kw):
+        if key in self.data:
+            return self.data[key]
+        else:
+            self.data[key] = data = creation_function(**kw)
+            return data
+
+    def put(self, key, value, **kw):
+        self.data[key] = value
+
+    def get(self, key, **kw):
+        return self.data[key]
+
+    def invalidate(self, key, **kw):
+        del self.data[key]
+
+
+register_plugin("plain", __name__, "PlainCacheImpl")
diff --git a/third_party/mako/mako/test/ext/test_babelplugin.py b/third_party/mako/mako/test/ext/test_babelplugin.py
index de3e46105772..ca12444567f9 100644
--- a/third_party/mako/mako/test/ext/test_babelplugin.py
+++ b/third_party/mako/mako/test/ext/test_babelplugin.py
@@ -1,103 +1,123 @@
 import io
 import os
+import unittest
 
-from mako.ext.babelplugin import extract
-from mako.testing.assertions import eq_
-from mako.testing.config import config
-from mako.testing.exclusions import requires_babel
-from mako.testing.fixtures import TemplateTest
+from mako import compat
+from .. import skip_if
+from .. import template_base
+from .. import TemplateTest
 
+try:
+    import babel.messages.extract as babel
+    from mako.ext.babelplugin import extract
 
-@requires_babel
-class PluginExtractTest:
+except ImportError:
+    babel = None
+
+
+def skip():
+    return skip_if(
+        lambda: not babel, "babel not installed: skipping babelplugin test"
+    )
+
+
+class Test_extract(unittest.TestCase):
+    @skip()
     def test_parse_python_expression(self):
-        input_ = io.BytesIO(b'<p>${_("Message")}</p>')
+        input_ = io.BytesIO(compat.b('<p>${_("Message")}</p>'))
         messages = list(extract(input_, ["_"], [], {}))
-        eq_(messages, [(1, "_", ("Message"), [])])
+        self.assertEqual(messages, [(1, "_", compat.u("Message"), [])])
 
+    @skip()
     def test_python_gettext_call(self):
-        input_ = io.BytesIO(b'<p>${_("Message")}</p>')
+        input_ = io.BytesIO(compat.b('<p>${_("Message")}</p>'))
         messages = list(extract(input_, ["_"], [], {}))
-        eq_(messages, [(1, "_", ("Message"), [])])
+        self.assertEqual(messages, [(1, "_", compat.u("Message"), [])])
 
+    @skip()
     def test_translator_comment(self):
         input_ = io.BytesIO(
-            b"""
+            compat.b(
+                """
         <p>
           ## TRANSLATORS: This is a comment.
           ${_("Message")}
         </p>"""
+            )
         )
         messages = list(extract(input_, ["_"], ["TRANSLATORS:"], {}))
-        eq_(
+        self.assertEqual(
             messages,
             [
                 (
                     4,
                     "_",
-                    ("Message"),
-                    [("TRANSLATORS: This is a comment.")],
+                    compat.u("Message"),
+                    [compat.u("TRANSLATORS: This is a comment.")],
                 )
             ],
         )
 
 
-@requires_babel
-class MakoExtractTest(TemplateTest):
+class ExtractMakoTestCase(TemplateTest):
+    @skip()
     def test_extract(self):
-        with open(
-            os.path.join(config.template_base, "gettext.mako")
-        ) as mako_tmpl:
-            messages = list(
-                extract(
-                    mako_tmpl,
-                    {"_": None, "gettext": None, "ungettext": (1, 2)},
-                    ["TRANSLATOR:"],
-                    {},
-                )
+        mako_tmpl = open(os.path.join(template_base, "gettext.mako"))
+        self.addCleanup(mako_tmpl.close)
+        messages = list(
+            extract(
+                mako_tmpl,
+                {"_": None, "gettext": None, "ungettext": (1, 2)},
+                ["TRANSLATOR:"],
+                {},
             )
-            expected = [
-                (1, "_", "Page arg 1", []),
-                (1, "_", "Page arg 2", []),
-                (10, "gettext", "Begin", []),
-                (14, "_", "Hi there!", ["TRANSLATOR: Hi there!"]),
-                (19, "_", "Hello", []),
-                (22, "_", "Welcome", []),
-                (25, "_", "Yo", []),
-                (36, "_", "The", ["TRANSLATOR: Ensure so and", "so, thanks"]),
-                (36, "ungettext", ("bunny", "bunnies", None), []),
-                (41, "_", "Goodbye", ["TRANSLATOR: Good bye"]),
-                (44, "_", "Babel", []),
-                (45, "ungettext", ("hella", "hellas", None), []),
-                (62, "_", "The", ["TRANSLATOR: Ensure so and", "so, thanks"]),
-                (62, "ungettext", ("bunny", "bunnies", None), []),
-                (68, "_", "Goodbye, really!", ["TRANSLATOR: HTML comment"]),
-                (71, "_", "P.S. byebye", []),
-                (77, "_", "Top", []),
-                (83, "_", "foo", []),
-                (83, "_", "hoho", []),
-                (85, "_", "bar", []),
-                (92, "_", "Inside a p tag", ["TRANSLATOR: <p> tag is ok?"]),
-                (95, "_", "Later in a p tag", ["TRANSLATOR: also this"]),
-                (99, "_", "No action at a distance.", []),
-            ]
-        eq_(expected, messages)
+        )
+        expected = [
+            (1, "_", "Page arg 1", []),
+            (1, "_", "Page arg 2", []),
+            (10, "gettext", "Begin", []),
+            (14, "_", "Hi there!", ["TRANSLATOR: Hi there!"]),
+            (19, "_", "Hello", []),
+            (22, "_", "Welcome", []),
+            (25, "_", "Yo", []),
+            (36, "_", "The", ["TRANSLATOR: Ensure so and", "so, thanks"]),
+            (36, "ungettext", ("bunny", "bunnies", None), []),
+            (41, "_", "Goodbye", ["TRANSLATOR: Good bye"]),
+            (44, "_", "Babel", []),
+            (45, "ungettext", ("hella", "hellas", None), []),
+            (62, "_", "The", ["TRANSLATOR: Ensure so and", "so, thanks"]),
+            (62, "ungettext", ("bunny", "bunnies", None), []),
+            (68, "_", "Goodbye, really!", ["TRANSLATOR: HTML comment"]),
+            (71, "_", "P.S. byebye", []),
+            (77, "_", "Top", []),
+            (83, "_", "foo", []),
+            (83, "_", "hoho", []),
+            (85, "_", "bar", []),
+            (92, "_", "Inside a p tag", ["TRANSLATOR: <p> tag is ok?"]),
+            (95, "_", "Later in a p tag", ["TRANSLATOR: also this"]),
+            (99, "_", "No action at a distance.", []),
+        ]
+        self.assertEqual(expected, messages)
 
+    @skip()
     def test_extract_utf8(self):
-        with open(
-            os.path.join(config.template_base, "gettext_utf8.mako"), "rb"
-        ) as mako_tmpl:
-            message = next(
-                extract(mako_tmpl, {"_", None}, [], {"encoding": "utf-8"})
-            )
-            assert message == (1, "_", "K\xf6ln", [])
+        mako_tmpl = open(
+            os.path.join(template_base, "gettext_utf8.mako"), "rb"
+        )
+        self.addCleanup(mako_tmpl.close)
+        message = next(
+            extract(mako_tmpl, set(["_", None]), [], {"encoding": "utf-8"})
+        )
+        assert message == (1, "_", u"K\xf6ln", [])
 
+    @skip()
     def test_extract_cp1251(self):
-        with open(
-            os.path.join(config.template_base, "gettext_cp1251.mako"), "rb"
-        ) as mako_tmpl:
-            message = next(
-                extract(mako_tmpl, {"_", None}, [], {"encoding": "cp1251"})
-            )
-            # "test" in Rusian. File encoding is cp1251 (aka "windows-1251")
-            assert message == (1, "_", "\u0442\u0435\u0441\u0442", [])
+        mako_tmpl = open(
+            os.path.join(template_base, "gettext_cp1251.mako"), "rb"
+        )
+        self.addCleanup(mako_tmpl.close)
+        message = next(
+            extract(mako_tmpl, set(["_", None]), [], {"encoding": "cp1251"})
+        )
+        # "test" in Rusian. File encoding is cp1251 (aka "windows-1251")
+        assert message == (1, "_", u"\u0442\u0435\u0441\u0442", [])
diff --git a/third_party/mako/mako/test/ext/test_linguaplugin.py b/third_party/mako/mako/test/ext/test_linguaplugin.py
index ae24f6755a51..a56396912253 100644
--- a/third_party/mako/mako/test/ext/test_linguaplugin.py
+++ b/third_party/mako/mako/test/ext/test_linguaplugin.py
@@ -1,38 +1,40 @@
 import os
 
-import pytest
+from .. import skip_if
+from .. import template_base
+from .. import TemplateTest
 
-from mako.ext.linguaplugin import LinguaMakoExtractor
-from mako.testing.assertions import eq_
-from mako.testing.config import config
-from mako.testing.exclusions import requires_lingua
-from mako.testing.fixtures import TemplateTest
+try:
+    import lingua
+except:
+    lingua = None
+
+if lingua is not None:
+    from mako.ext.linguaplugin import LinguaMakoExtractor
+    from lingua.extractors import register_extractors
 
 
 class MockOptions:
     keywords = []
     domain = None
-    comment_tag = True
 
 
-@requires_lingua
-class MakoExtractTest(TemplateTest):
-    @pytest.fixture(autouse=True)
-    def register_lingua_extractors(self):
-        from lingua.extractors import register_extractors
+def skip():
+    return skip_if(
+        lambda: not lingua, "lingua not installed: skipping linguaplugin test"
+    )
 
-        register_extractors()
 
+class ExtractMakoTestCase(TemplateTest):
+    @skip()
     def test_extract(self):
+        register_extractors()
         plugin = LinguaMakoExtractor({"comment-tags": "TRANSLATOR"})
         messages = list(
-            plugin(
-                os.path.join(config.template_base, "gettext.mako"),
-                MockOptions(),
-            )
+            plugin(os.path.join(template_base, "gettext.mako"), MockOptions())
         )
         msgids = [(m.msgid, m.msgid_plural) for m in messages]
-        eq_(
+        self.assertEqual(
             msgids,
             [
                 ("Page arg 1", None),
@@ -52,9 +54,9 @@ class MakoExtractTest(TemplateTest):
                 ("Goodbye, really!", None),
                 ("P.S. byebye", None),
                 ("Top", None),
-                ("foo", None),
+                (u"foo", None),
                 ("hoho", None),
-                ("bar", None),
+                (u"bar", None),
                 ("Inside a p tag", None),
                 ("Later in a p tag", None),
                 ("No action at a distance.", None),
diff --git a/third_party/mako/mako/test/module_to_import.py b/third_party/mako/mako/test/module_to_import.py
deleted file mode 100644
index 11ffb986471e..000000000000
--- a/third_party/mako/mako/test/module_to_import.py
+++ /dev/null
@@ -1,2 +0,0 @@
-def some_function():
-    pass
diff --git a/third_party/mako/mako/test/templates/chs_unicode.html b/third_party/mako/mako/test/templates/chs_unicode.html
new file mode 100644
index 000000000000..7b897e903cfb
--- /dev/null
+++ b/third_party/mako/mako/test/templates/chs_unicode.html
@@ -0,0 +1,11 @@
+## -*- coding:utf-8 -*-
+<%
+ msg = u'新中国的主席'
+%>
+
+<%def name="welcome(who, place=u'北京')">
+Welcome ${who} to ${place}.
+</%def>
+
+${name} 是 ${msg}<br/>
+${welcome(u'你')}
diff --git a/third_party/mako/mako/test/templates/foo/modtest.html.py b/third_party/mako/mako/test/templates/foo/modtest.html.py
index c35420f52fc4..e6fc8d89eed6 100644
--- a/third_party/mako/mako/test/templates/foo/modtest.html.py
+++ b/third_party/mako/mako/test/templates/foo/modtest.html.py
@@ -1,25 +1,25 @@
-from mako import cache
-from mako import runtime
-
+from mako import runtime, filters, cache
 UNDEFINED = runtime.UNDEFINED
 __M_dict_builtin = dict
 __M_locals_builtin = locals
 _magic_number = 5
 _modified_time = 1267565427.7968459
-_template_filename = "/Users/classic/dev/mako/test/templates/modtest.html"
-_template_uri = "/modtest.html"
-_template_cache = cache.Cache(__name__, _modified_time)
-_source_encoding = None
+_template_filename='/Users/classic/dev/mako/test/templates/modtest.html'
+_template_uri='/modtest.html'
+_template_cache=cache.Cache(__name__, _modified_time)
+_source_encoding=None
 _exports = []
 
 
-def render_body(context, **pageargs):
+def render_body(context,**pageargs):
     context.caller_stack._push_frame()
     try:
         __M_locals = __M_dict_builtin(pageargs=pageargs)
         __M_writer = context.writer()
         # SOURCE LINE 1
-        __M_writer("this is a test")
-        return ""
+        __M_writer('this is a test')
+        return ''
     finally:
         context.caller_stack._pop_frame()
+
+
diff --git a/third_party/mako/mako/test/templates/read_unicode.html b/third_party/mako/mako/test/templates/read_unicode.html
new file mode 100644
index 000000000000..50f00c323e5d
--- /dev/null
+++ b/third_party/mako/mako/test/templates/read_unicode.html
@@ -0,0 +1,10 @@
+<% 
+try:
+    file_content = open(path)
+except:
+    raise "Should never execute here"
+doc_content = ''.join(file_content.readlines())
+file_content.close()
+%>
+
+${unicode(doc_content, encoding='utf-8')}
diff --git a/third_party/mako/mako/test/templates/runtimeerr.html b/third_party/mako/mako/test/templates/runtimeerr.html
new file mode 100644
index 000000000000..6b36b9561ad2
--- /dev/null
+++ b/third_party/mako/mako/test/templates/runtimeerr.html
@@ -0,0 +1,4 @@
+<%
+    print y
+    y = 10
+%>
\ No newline at end of file
diff --git a/third_party/mako/mako/test/templates/subdir/foo/modtest.html.py b/third_party/mako/mako/test/templates/subdir/foo/modtest.html.py
index 9df72e01912b..b0f50c762f3c 100644
--- a/third_party/mako/mako/test/templates/subdir/foo/modtest.html.py
+++ b/third_party/mako/mako/test/templates/subdir/foo/modtest.html.py
@@ -1,27 +1,25 @@
-from mako import cache
-from mako import runtime
-
+from mako import runtime, filters, cache
 UNDEFINED = runtime.UNDEFINED
 __M_dict_builtin = dict
 __M_locals_builtin = locals
 _magic_number = 5
 _modified_time = 1267565427.799504
-_template_filename = (
-    "/Users/classic/dev/mako/test/templates/subdir/modtest.html"
-)
-_template_uri = "/subdir/modtest.html"
-_template_cache = cache.Cache(__name__, _modified_time)
-_source_encoding = None
+_template_filename='/Users/classic/dev/mako/test/templates/subdir/modtest.html'
+_template_uri='/subdir/modtest.html'
+_template_cache=cache.Cache(__name__, _modified_time)
+_source_encoding=None
 _exports = []
 
 
-def render_body(context, **pageargs):
+def render_body(context,**pageargs):
     context.caller_stack._push_frame()
     try:
         __M_locals = __M_dict_builtin(pageargs=pageargs)
         __M_writer = context.writer()
         # SOURCE LINE 1
-        __M_writer("this is a test")
-        return ""
+        __M_writer('this is a test')
+        return ''
     finally:
         context.caller_stack._pop_frame()
+
+
diff --git a/third_party/mako/mako/test/templates/unicode_arguments.html b/third_party/mako/mako/test/templates/unicode_arguments.html
new file mode 100644
index 000000000000..e6d7c2ca598b
--- /dev/null
+++ b/third_party/mako/mako/test/templates/unicode_arguments.html
@@ -0,0 +1,9 @@
+
+<%def name="my_def(x)">
+    x is: ${x}
+</%def>
+
+${my_def(u'drôle de petite voix m’a réveillé')}
+<%self:my_def x='drôle de petite voix m’a réveillé'/>
+<%self:my_def x="${u'drôle de petite voix m’a réveillé'}"/>
+<%call expr="my_def(u'drôle de petite voix m’a réveillé')"/>
diff --git a/third_party/mako/mako/test/templates/unicode_code.html b/third_party/mako/mako/test/templates/unicode_code.html
new file mode 100644
index 000000000000..700c043ebf2c
--- /dev/null
+++ b/third_party/mako/mako/test/templates/unicode_code.html
@@ -0,0 +1,7 @@
+## -*- coding: utf-8 -*-
+<%
+    x = u"drôle de petite voix m’a réveillé."
+%>
+% if x==u"drôle de petite voix m’a réveillé.":
+    hi, ${x}
+% endif
diff --git a/third_party/mako/mako/test/templates/unicode_expr.html b/third_party/mako/mako/test/templates/unicode_expr.html
new file mode 100644
index 000000000000..cad75226665c
--- /dev/null
+++ b/third_party/mako/mako/test/templates/unicode_expr.html
@@ -0,0 +1,2 @@
+## -*- coding: utf-8 -*-
+${u"Alors vous imaginez ma surprise, au lever du jour, quand une drôle de petite voix m’a réveillé. Elle disait: « S’il vous plaît… dessine-moi un mouton! »"}
diff --git a/third_party/mako/mako/test/test_ast.py b/third_party/mako/mako/test/test_ast.py
index 6b3a3e2f0d92..ce6c082b5ac6 100644
--- a/third_party/mako/mako/test/test_ast.py
+++ b/third_party/mako/mako/test/test_ast.py
@@ -1,13 +1,17 @@
+import unittest
+
 from mako import ast
+from mako import compat
 from mako import exceptions
 from mako import pyparser
-from mako.testing.assertions import assert_raises
-from mako.testing.assertions import eq_
+from test import eq_
+from test import requires_python_2
+from test import requires_python_3
 
 exception_kwargs = {"source": "", "lineno": 0, "pos": 0, "filename": ""}
 
 
-class AstParseTest:
+class AstParseTest(unittest.TestCase):
     def test_locate_identifiers(self):
         """test the location of identifiers in a python code string"""
         code = """
@@ -26,15 +30,17 @@ for lar in (1,2,3):
         parsed = ast.PythonCode(code, **exception_kwargs)
         eq_(
             parsed.declared_identifiers,
-            {"a", "b", "c", "g", "h", "i", "u", "k", "j", "gh", "lar", "x"},
+            set(
+                ["a", "b", "c", "g", "h", "i", "u", "k", "j", "gh", "lar", "x"]
+            ),
         )
         eq_(
             parsed.undeclared_identifiers,
-            {"x", "q", "foo", "gah", "blah"},
+            set(["x", "q", "foo", "gah", "blah"]),
         )
 
         parsed = ast.PythonCode("x + 5 * (y-z)", **exception_kwargs)
-        assert parsed.undeclared_identifiers == {"x", "y", "z"}
+        assert parsed.undeclared_identifiers == set(["x", "y", "z"])
         assert parsed.declared_identifiers == set()
 
     def test_locate_identifiers_2(self):
@@ -48,10 +54,10 @@ for x in data:
     result.append(x+7)
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.undeclared_identifiers, {"get_data"})
+        eq_(parsed.undeclared_identifiers, set(["get_data"]))
         eq_(
             parsed.declared_identifiers,
-            {"result", "data", "x", "hoho", "foobar", "foo", "yaya"},
+            set(["result", "data", "x", "hoho", "foobar", "foo", "yaya"]),
         )
 
     def test_locate_identifiers_3(self):
@@ -65,7 +71,7 @@ for y in range(1, y):
 (q for q in range (1, q))
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.undeclared_identifiers, {"x", "y", "z", "q", "range"})
+        eq_(parsed.undeclared_identifiers, set(["x", "y", "z", "q", "range"]))
 
     def test_locate_identifiers_4(self):
         code = """
@@ -75,8 +81,8 @@ def mydef(mydefarg):
     print("mda is", mydefarg)
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.undeclared_identifiers, {"y"})
-        eq_(parsed.declared_identifiers, {"mydef", "x"})
+        eq_(parsed.undeclared_identifiers, set(["y"]))
+        eq_(parsed.declared_identifiers, set(["mydef", "x"]))
 
     def test_locate_identifiers_5(self):
         code = """
@@ -86,7 +92,7 @@ except:
     print(y)
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.undeclared_identifiers, {"x", "y"})
+        eq_(parsed.undeclared_identifiers, set(["x", "y"]))
 
     def test_locate_identifiers_6(self):
         code = """
@@ -94,7 +100,7 @@ def foo():
     return bar()
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.undeclared_identifiers, {"bar"})
+        eq_(parsed.undeclared_identifiers, set(["bar"]))
 
         code = """
 def lala(x, y):
@@ -102,8 +108,8 @@ def lala(x, y):
 print(x)
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.undeclared_identifiers, {"z", "x"})
-        eq_(parsed.declared_identifiers, {"lala"})
+        eq_(parsed.undeclared_identifiers, set(["z", "x"]))
+        eq_(parsed.declared_identifiers, set(["lala"]))
 
         code = """
 def lala(x, y):
@@ -113,26 +119,26 @@ def lala(x, y):
 print(z)
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.undeclared_identifiers, {"z"})
-        eq_(parsed.declared_identifiers, {"lala"})
+        eq_(parsed.undeclared_identifiers, set(["z"]))
+        eq_(parsed.declared_identifiers, set(["lala"]))
 
     def test_locate_identifiers_7(self):
         code = """
 import foo.bar
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.declared_identifiers, {"foo"})
+        eq_(parsed.declared_identifiers, set(["foo"]))
         eq_(parsed.undeclared_identifiers, set())
 
     def test_locate_identifiers_8(self):
         code = """
-class Hi:
+class Hi(object):
     foo = 7
     def hoho(self):
         x = 5
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.declared_identifiers, {"Hi"})
+        eq_(parsed.declared_identifiers, set(["Hi"]))
         eq_(parsed.undeclared_identifiers, set())
 
     def test_locate_identifiers_9(self):
@@ -140,15 +146,15 @@ class Hi:
     ",".join([t for t in ("a", "b", "c")])
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.declared_identifiers, {"t"})
-        eq_(parsed.undeclared_identifiers, {"t"})
+        eq_(parsed.declared_identifiers, set(["t"]))
+        eq_(parsed.undeclared_identifiers, set(["t"]))
 
         code = """
     [(val, name) for val, name in x]
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.declared_identifiers, {"val", "name"})
-        eq_(parsed.undeclared_identifiers, {"val", "name", "x"})
+        eq_(parsed.declared_identifiers, set(["val", "name"]))
+        eq_(parsed.undeclared_identifiers, set(["val", "name", "x"]))
 
     def test_locate_identifiers_10(self):
         code = """
@@ -164,7 +170,7 @@ def x(q):
     return q + 5
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.declared_identifiers, {"x"})
+        eq_(parsed.declared_identifiers, set(["x"]))
         eq_(parsed.undeclared_identifiers, set())
 
     def test_locate_identifiers_12(self):
@@ -175,32 +181,44 @@ def foo():
         t = s
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.declared_identifiers, {"foo"})
+        eq_(parsed.declared_identifiers, set(["foo"]))
         eq_(parsed.undeclared_identifiers, set())
 
     def test_locate_identifiers_13(self):
         code = """
 def foo():
-    class Bat:
+    class Bat(object):
         pass
     Bat
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.declared_identifiers, {"foo"})
+        eq_(parsed.declared_identifiers, set(["foo"]))
         eq_(parsed.undeclared_identifiers, set())
 
     def test_locate_identifiers_14(self):
         code = """
 def foo():
-    class Bat:
+    class Bat(object):
         pass
     Bat
 
 print(Bat)
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.declared_identifiers, {"foo"})
-        eq_(parsed.undeclared_identifiers, {"Bat"})
+        eq_(parsed.declared_identifiers, set(["foo"]))
+        eq_(parsed.undeclared_identifiers, set(["Bat"]))
+
+    @requires_python_2
+    def test_locate_identifiers_15(self):
+        code = """
+def t1((x,y)):
+    return x+5, y+4
+
+t2 = lambda (x,y):(x+5, y+4)
+"""
+        parsed = ast.PythonCode(code, **exception_kwargs)
+        eq_(parsed.declared_identifiers, set(["t1", "t2"]))
+        eq_(parsed.undeclared_identifiers, set())
 
     def test_locate_identifiers_16(self):
         code = """
@@ -210,7 +228,7 @@ except Exception as e:
     print(y)
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.undeclared_identifiers, {"x", "y", "Exception"})
+        eq_(parsed.undeclared_identifiers, set(["x", "y", "Exception"]))
 
     def test_locate_identifiers_17(self):
         code = """
@@ -220,38 +238,43 @@ except (Foo, Bar) as e:
     print(y)
 """
         parsed = ast.PythonCode(code, **exception_kwargs)
-        eq_(parsed.undeclared_identifiers, {"x", "y", "Foo", "Bar"})
+        eq_(parsed.undeclared_identifiers, set(["x", "y", "Foo", "Bar"]))
 
     def test_no_global_imports(self):
         code = """
 from foo import *
 import x as bar
 """
-        assert_raises(
+        self.assertRaises(
             exceptions.CompileException,
             ast.PythonCode,
             code,
-            **exception_kwargs,
+            **exception_kwargs
         )
 
     def test_python_fragment(self):
         parsed = ast.PythonFragment("for x in foo:", **exception_kwargs)
-        eq_(parsed.declared_identifiers, {"x"})
-        eq_(parsed.undeclared_identifiers, {"foo"})
+        eq_(parsed.declared_identifiers, set(["x"]))
+        eq_(parsed.undeclared_identifiers, set(["foo"]))
 
         parsed = ast.PythonFragment("try:", **exception_kwargs)
 
-        parsed = ast.PythonFragment(
-            "except MyException as e:", **exception_kwargs
-        )
-        eq_(parsed.declared_identifiers, {"e"})
-        eq_(parsed.undeclared_identifiers, {"MyException"})
+        if compat.py3k:
+            parsed = ast.PythonFragment(
+                "except MyException as e:", **exception_kwargs
+            )
+        else:
+            parsed = ast.PythonFragment(
+                "except MyException, e:", **exception_kwargs
+            )
+        eq_(parsed.declared_identifiers, set(["e"]))
+        eq_(parsed.undeclared_identifiers, set(["MyException"]))
 
     def test_argument_list(self):
         parsed = ast.ArgumentList(
             "3, 5, 'hi', x+5, " "context.get('lala')", **exception_kwargs
         )
-        eq_(parsed.undeclared_identifiers, {"x", "context"})
+        eq_(parsed.undeclared_identifiers, set(["x", "context"]))
         eq_(
             [x for x in parsed.args],
             ["3", "5", "'hi'", "(x + 5)", "context.get('lala')"],
@@ -276,6 +299,7 @@ import x as bar
         eq_(parsed.argnames, ["a", "b", "c", "args"])
         eq_(parsed.kwargnames, ["kwargs"])
 
+    @requires_python_3
     def test_function_decl_3(self):
         """test getting the arguments from a fancy py3k function"""
         code = "def foo(a, b, *c, d, e, **f):pass"
@@ -289,7 +313,7 @@ import x as bar
         x = 1
         y = 2
 
-        class F:
+        class F(object):
             def bar(self, a, b):
                 return a + b
 
diff --git a/third_party/mako/mako/test/test_block.py b/third_party/mako/mako/test/test_block.py
index be2fbf718581..0cbe347364f6 100644
--- a/third_party/mako/mako/test/test_block.py
+++ b/third_party/mako/mako/test/test_block.py
@@ -1,9 +1,9 @@
 from mako import exceptions
 from mako.lookup import TemplateLookup
 from mako.template import Template
-from mako.testing.assertions import assert_raises_message
-from mako.testing.fixtures import TemplateTest
-from mako.testing.helpers import result_lines
+from test import assert_raises_message
+from test import TemplateTest
+from test.util import result_lines
 
 
 class BlockTest(TemplateTest):
diff --git a/third_party/mako/mako/test/test_cache.py b/third_party/mako/mako/test/test_cache.py
index dd415d4958c1..d662872c0f3f 100644
--- a/third_party/mako/mako/test/test_cache.py
+++ b/third_party/mako/mako/test/test_cache.py
@@ -1,21 +1,23 @@
 import time
+import unittest
 
 from mako import lookup
 from mako.cache import CacheImpl
 from mako.cache import register_plugin
+from mako.compat import py27
+from mako.ext import beaker_cache
 from mako.lookup import TemplateLookup
 from mako.template import Template
-from mako.testing.assertions import eq_
-from mako.testing.config import config
-from mako.testing.exclusions import requires_beaker
-from mako.testing.exclusions import requires_dogpile_cache
-from mako.testing.helpers import result_lines
+from test import eq_
+from test import module_base
+from test import TemplateTest
+from test.util import result_lines
 
+if beaker_cache.has_beaker:
+    import beaker
 
-module_base = str(config.module_base)
 
-
-class SimpleBackend:
+class SimpleBackend(object):
     def __init__(self):
         self.cache = {}
 
@@ -31,9 +33,9 @@ class SimpleBackend:
     def get_or_create(self, key, creation_function, **kw):
         if key in self.cache:
             return self.cache[key]
-
-        self.cache[key] = value = creation_function()
-        return value
+        else:
+            self.cache[key] = value = creation_function()
+            return value
 
 
 class MockCacheImpl(CacheImpl):
@@ -79,7 +81,7 @@ class MockCacheImpl(CacheImpl):
 register_plugin("mock", __name__, "MockCacheImpl")
 
 
-class CacheTest:
+class CacheTest(TemplateTest):
 
     real_backend = "simple"
 
@@ -599,7 +601,7 @@ class CacheTest:
         assert m.kwargs["context"].get("x") == "bar"
 
 
-class RealBackendMixin:
+class RealBackendTest(object):
     def test_cache_uses_current_context(self):
         t = Template(
             """
@@ -642,17 +644,23 @@ class RealBackendMixin:
         eq_(r3, ["short term 6", "long term 5", "none 7"])
 
 
-@requires_beaker
-class BeakerCacheTest(RealBackendMixin, CacheTest):
+class BeakerCacheTest(RealBackendTest, CacheTest):
     real_backend = "beaker"
 
+    def setUp(self):
+        if not beaker_cache.has_beaker:
+            raise unittest.SkipTest("Beaker is required for these tests.")
+        if not py27:
+            raise unittest.SkipTest("newer beakers not working w/ py26")
+
     def _install_mock_cache(self, template, implname=None):
         template.cache_args["manager"] = self._regions()
-        return super()._install_mock_cache(template, implname)
+        impl = super(BeakerCacheTest, self)._install_mock_cache(
+            template, implname
+        )
+        return impl
 
     def _regions(self):
-        import beaker
-
         return beaker.cache.CacheManager(
             cache_regions={
                 "short": {"expire": 1, "type": "memory"},
@@ -661,14 +669,24 @@ class BeakerCacheTest(RealBackendMixin, CacheTest):
         )
 
 
-@requires_dogpile_cache
-class DogpileCacheTest(RealBackendMixin, CacheTest):
+class DogpileCacheTest(RealBackendTest, CacheTest):
     real_backend = "dogpile.cache"
 
+    def setUp(self):
+        try:
+            import dogpile.cache  # noqa
+        except ImportError:
+            raise unittest.SkipTest(
+                "dogpile.cache is required to run these tests"
+            )
+
     def _install_mock_cache(self, template, implname=None):
         template.cache_args["regions"] = self._regions()
         template.cache_args.setdefault("region", "short")
-        return super()._install_mock_cache(template, implname)
+        impl = super(DogpileCacheTest, self)._install_mock_cache(
+            template, implname
+        )
+        return impl
 
     def _regions(self):
         from dogpile.cache import make_region
diff --git a/third_party/mako/mako/test/test_call.py b/third_party/mako/mako/test/test_call.py
index 4dea2b355832..36d15dccb047 100644
--- a/third_party/mako/mako/test/test_call.py
+++ b/third_party/mako/mako/test/test_call.py
@@ -1,8 +1,8 @@
 from mako.template import Template
-from mako.testing.assertions import eq_
-from mako.testing.fixtures import TemplateTest
-from mako.testing.helpers import flatten_result
-from mako.testing.helpers import result_lines
+from test import eq_
+from test import TemplateTest
+from test.util import flatten_result
+from test.util import result_lines
 
 
 class CallTest(TemplateTest):
diff --git a/third_party/mako/mako/test/test_cmd.py b/third_party/mako/mako/test/test_cmd.py
index 785c652304a5..ac0db25a68b9 100644
--- a/third_party/mako/mako/test/test_cmd.py
+++ b/third_party/mako/mako/test/test_cmd.py
@@ -1,13 +1,14 @@
+from __future__ import with_statement
+
 from contextlib import contextmanager
 import os
-from unittest import mock
 
 from mako.cmd import cmdline
-from mako.testing.assertions import eq_
-from mako.testing.assertions import expect_raises
-from mako.testing.assertions import expect_raises_message
-from mako.testing.config import config
-from mako.testing.fixtures import TemplateTest
+from test import eq_
+from test import mock
+from test import raises
+from test import template_base
+from test import TemplateTest
 
 
 class CmdTest(TemplateTest):
@@ -31,7 +32,7 @@ class CmdTest(TemplateTest):
             "sys.stdin", mock.Mock(read=mock.Mock(return_value="${x"))
         ):
             with self._capture_output_fixture("stderr") as stderr:
-                with expect_raises(SystemExit):
+                with raises(SystemExit):
                     cmdline(["--var", "x=5", "-"])
 
             assert (
@@ -44,7 +45,7 @@ class CmdTest(TemplateTest):
             "sys.stdin", mock.Mock(read=mock.Mock(return_value="${q}"))
         ):
             with self._capture_output_fixture("stderr") as stderr:
-                with expect_raises(SystemExit):
+                with raises(SystemExit):
                     cmdline(["--var", "x=5", "-"])
 
             assert "NameError: Undefined" in stderr.write.mock_calls[0][1][0]
@@ -53,23 +54,19 @@ class CmdTest(TemplateTest):
     def test_file_success(self):
         with self._capture_output_fixture() as stdout:
             cmdline(
-                [
-                    "--var",
-                    "x=5",
-                    os.path.join(config.template_base, "cmd_good.mako"),
-                ]
+                ["--var", "x=5", os.path.join(template_base, "cmd_good.mako")]
             )
 
         eq_(stdout.write.mock_calls[0][1][0], "hello world 5")
 
     def test_file_syntax_err(self):
         with self._capture_output_fixture("stderr") as stderr:
-            with expect_raises(SystemExit):
+            with raises(SystemExit):
                 cmdline(
                     [
                         "--var",
                         "x=5",
-                        os.path.join(config.template_base, "cmd_syntax.mako"),
+                        os.path.join(template_base, "cmd_syntax.mako"),
                     ]
                 )
 
@@ -78,12 +75,12 @@ class CmdTest(TemplateTest):
 
     def test_file_rt_err(self):
         with self._capture_output_fixture("stderr") as stderr:
-            with expect_raises(SystemExit):
+            with raises(SystemExit):
                 cmdline(
                     [
                         "--var",
                         "x=5",
-                        os.path.join(config.template_base, "cmd_runtime.mako"),
+                        os.path.join(template_base, "cmd_runtime.mako"),
                     ]
                 )
 
@@ -91,7 +88,5 @@ class CmdTest(TemplateTest):
         assert "Traceback" in stderr.write.mock_calls[0][1][0]
 
     def test_file_notfound(self):
-        with expect_raises_message(
-            SystemExit, "error: can't find fake.lalala"
-        ):
+        with raises(SystemExit, "error: can't find fake.lalala"):
             cmdline(["--var", "x=5", "fake.lalala"])
diff --git a/third_party/mako/mako/test/test_decorators.py b/third_party/mako/mako/test/test_decorators.py
index 68ea903c6ebc..195a6366d2da 100644
--- a/third_party/mako/mako/test/test_decorators.py
+++ b/third_party/mako/mako/test/test_decorators.py
@@ -1,8 +1,10 @@
+import unittest
+
 from mako.template import Template
-from mako.testing.helpers import flatten_result
+from test.util import flatten_result
 
 
-class DecoratorTest:
+class DecoratorTest(unittest.TestCase):
     def test_toplevel(self):
         template = Template(
             """
diff --git a/third_party/mako/mako/test/test_def.py b/third_party/mako/mako/test/test_def.py
index fd964332bbbc..99b929dd8d8f 100644
--- a/third_party/mako/mako/test/test_def.py
+++ b/third_party/mako/mako/test/test_def.py
@@ -1,10 +1,12 @@
+from mako import compat
 from mako import lookup
 from mako.template import Template
-from mako.testing.assertions import assert_raises
-from mako.testing.assertions import eq_
-from mako.testing.fixtures import TemplateTest
-from mako.testing.helpers import flatten_result
-from mako.testing.helpers import result_lines
+from test import assert_raises
+from test import eq_
+from test import requires_python_3
+from test import TemplateTest
+from test.util import flatten_result
+from test.util import result_lines
 
 
 class DefTest(TemplateTest):
@@ -47,6 +49,7 @@ class DefTest(TemplateTest):
             """hello mycomp hi, 5, 6""",
         )
 
+    @requires_python_3
     def test_def_py3k_args(self):
         template = Template(
             """
@@ -83,8 +86,12 @@ class DefTest(TemplateTest):
 """
         )
         # check that "a" is declared in "b", but not in "c"
-        assert "a" not in template.module.render_c.__code__.co_varnames
-        assert "a" in template.module.render_b.__code__.co_varnames
+        if compat.py3k:
+            assert "a" not in template.module.render_c.__code__.co_varnames
+            assert "a" in template.module.render_b.__code__.co_varnames
+        else:
+            assert "a" not in template.module.render_c.func_code.co_varnames
+            assert "a" in template.module.render_b.func_code.co_varnames
 
         # then test output
         eq_(flatten_result(template.render()), "im b and heres a: im a")
diff --git a/third_party/mako/mako/test/test_exceptions.py b/third_party/mako/mako/test/test_exceptions.py
index b1930c563e45..2ec46cf42694 100644
--- a/third_party/mako/mako/test/test_exceptions.py
+++ b/third_party/mako/mako/test/test_exceptions.py
@@ -1,12 +1,15 @@
+# -*- coding: utf-8 -*-
 import sys
 
+from mako import compat
 from mako import exceptions
+from mako.compat import u
 from mako.lookup import TemplateLookup
 from mako.template import Template
-from mako.testing.exclusions import requires_no_pygments_exceptions
-from mako.testing.exclusions import requires_pygments_14
-from mako.testing.fixtures import TemplateTest
-from mako.testing.helpers import result_lines
+from test import requires_no_pygments_exceptions
+from test import requires_pygments_14
+from test import TemplateTest
+from test.util import result_lines
 
 
 class ExceptionsTest(TemplateTest):
@@ -65,28 +68,53 @@ class ExceptionsTest(TemplateTest):
         """test the html_error_template with a Template containing UTF-8
         chars"""
 
-        code = """# -*- coding: utf-8 -*-
+        if compat.py3k:
+            code = """# -*- coding: utf-8 -*-
 % if 2 == 2: /an error
 ${'привет'}
 % endif
+"""
+        else:
+            code = """# -*- coding: utf-8 -*-
+% if 2 == 2: /an error
+${u'привет'}
+% endif
 """
         try:
             template = Template(code)
             template.render_unicode()
         except exceptions.CompileException:
             html_error = exceptions.html_error_template().render()
-            assert (
-                "CompileException: Fragment &#39;if 2 == 2: /an "
-                "error&#39; is not a partial control statement "
-                "at line: 2 char: 1"
-            ).encode(
-                sys.getdefaultencoding(), "htmlentityreplace"
-            ) in html_error
-
-            assert (
-                "".encode(sys.getdefaultencoding(), "htmlentityreplace")
-                in html_error
-            )
+            if compat.py3k:
+                assert (
+                    (
+                        "CompileException: Fragment &#39;if 2 == 2: /an "
+                        "error&#39; is not a partial control statement "
+                        "at line: 2 char: 1"
+                    ).encode(sys.getdefaultencoding(), "htmlentityreplace")
+                    in html_error
+                )
+            else:
+                assert (
+                    "CompileException: Fragment &#39;if 2 == 2: /an "
+                    "error&#39; is not a partial control statement "
+                    "at line: 2 char: 1"
+                ) in html_error
+
+            if compat.py3k:
+                assert (
+                    "".encode(sys.getdefaultencoding(), "htmlentityreplace")
+                    in html_error
+                )
+            else:
+                assert (
+                    "&#39;"
+                    "&#x43F;&#x440;&#x438;&#x432;&#x435;&#x442;"
+                    '&#39;</span><span class="cp">}</span>'.encode(
+                        sys.getdefaultencoding(), "htmlentityreplace"
+                    )
+                    in html_error
+                )
         else:
             assert False, (
                 "This function should trigger a CompileException, "
@@ -98,29 +126,53 @@ ${'привет'}
         """test the html_error_template with a Template containing UTF-8
         chars"""
 
-        code = """# -*- coding: utf-8 -*-
+        if compat.py3k:
+            code = """# -*- coding: utf-8 -*-
 % if 2 == 2: /an error
 ${'привет'}
 % endif
+"""
+        else:
+            code = """# -*- coding: utf-8 -*-
+% if 2 == 2: /an error
+${u'привет'}
+% endif
 """
         try:
             template = Template(code)
             template.render_unicode()
         except exceptions.CompileException:
             html_error = exceptions.html_error_template().render()
-            assert (
-                "CompileException: Fragment &#39;if 2 == 2: /an "
-                "error&#39; is not a partial control statement "
-                "at line: 2 char: 1"
-            ).encode(
-                sys.getdefaultencoding(), "htmlentityreplace"
-            ) in html_error
-            assert (
-                "${&#39;привет&#39;}".encode(
-                    sys.getdefaultencoding(), "htmlentityreplace"
+            if compat.py3k:
+                assert (
+                    (
+                        "CompileException: Fragment &#39;if 2 == 2: /an "
+                        "error&#39; is not a partial control statement "
+                        "at line: 2 char: 1"
+                    ).encode(sys.getdefaultencoding(), "htmlentityreplace")
+                    in html_error
+                )
+            else:
+                assert (
+                    "CompileException: Fragment &#39;if 2 == 2: /an "
+                    "error&#39; is not a partial control statement "
+                    "at line: 2 char: 1"
+                ) in html_error
+
+            if compat.py3k:
+                assert (
+                    "${&#39;привет&#39;}".encode(
+                        sys.getdefaultencoding(), "htmlentityreplace"
+                    )
+                    in html_error
+                )
+            else:
+                assert (
+                    u("${u&#39;привет&#39;}").encode(
+                        sys.getdefaultencoding(), "htmlentityreplace"
+                    )
+                    in html_error
                 )
-                in html_error
-            )
         else:
             assert False, (
                 "This function should trigger a CompileException, "
@@ -137,21 +189,30 @@ ${'привет'}
 
     def test_py_utf8_html_error_template(self):
         try:
-            foo = "日本"  # noqa
+            foo = u("日本")  # noqa
             raise RuntimeError("test")
         except:
             html_error = exceptions.html_error_template().render()
-            assert "RuntimeError: test" in html_error.decode("utf-8")
-            assert "foo = &quot;日本&quot;" in html_error.decode(
-                "utf-8"
-            ) or "foo = &#34;日本&#34;" in html_error.decode("utf-8")
+            if compat.py3k:
+                assert "RuntimeError: test" in html_error.decode("utf-8")
+                assert "foo = u(&quot;日本&quot;)" in html_error.decode(
+                    "utf-8"
+                ) or "foo = u(&#34;日本&#34;)" in html_error.decode("utf-8")
+            else:
+                assert "RuntimeError: test" in html_error
+                assert (
+                    "foo = u(&quot;&#x65E5;&#x672C;&quot;)" in html_error
+                    or "foo = u(&#34;&#x65E5;&#x672C;&#34;)" in html_error
+                )
 
     def test_py_unicode_error_html_error_template(self):
         try:
-            raise RuntimeError("日本")
+            raise RuntimeError(u("日本"))
         except:
             html_error = exceptions.html_error_template().render()
-            assert "RuntimeError: 日本".encode("ascii", "ignore") in html_error
+            assert (
+                u("RuntimeError: 日本").encode("ascii", "ignore") in html_error
+            )
 
     @requires_pygments_14
     def test_format_exceptions_pygments(self):
@@ -203,33 +264,60 @@ ${foobar}
     @requires_pygments_14
     def test_utf8_format_exceptions_pygments(self):
         """test that htmlentityreplace formatting is applied to
-        exceptions reported with format_exceptions=True"""
+           exceptions reported with format_exceptions=True"""
 
         l = TemplateLookup(format_exceptions=True)
-        l.put_string(
-            "foo.html", """# -*- coding: utf-8 -*-\n${'привет' + foobar}"""
-        )
+        if compat.py3k:
+            l.put_string(
+                "foo.html", """# -*- coding: utf-8 -*-\n${'привет' + foobar}"""
+            )
+        else:
+            l.put_string(
+                "foo.html",
+                """# -*- coding: utf-8 -*-\n${u'привет' + foobar}""",
+            )
 
-        assert "&#39;привет&#39;</span>" in l.get_template(
-            "foo.html"
-        ).render().decode("utf-8")
+        if compat.py3k:
+            assert "&#39;привет&#39;</span>" in l.get_template(
+                "foo.html"
+            ).render().decode("utf-8")
+        else:
+            assert (
+                "&#39;&#x43F;&#x440;&#x438;&#x432;"
+                "&#x435;&#x442;&#39;</span>"
+            ) in l.get_template("foo.html").render().decode("utf-8")
 
     @requires_no_pygments_exceptions
     def test_utf8_format_exceptions_no_pygments(self):
         """test that htmlentityreplace formatting is applied to
-        exceptions reported with format_exceptions=True"""
+           exceptions reported with format_exceptions=True"""
 
         l = TemplateLookup(format_exceptions=True)
-        l.put_string(
-            "foo.html", """# -*- coding: utf-8 -*-\n${'привет' + foobar}"""
-        )
+        if compat.py3k:
+            l.put_string(
+                "foo.html", """# -*- coding: utf-8 -*-\n${'привет' + foobar}"""
+            )
+        else:
+            l.put_string(
+                "foo.html",
+                """# -*- coding: utf-8 -*-\n${u'привет' + foobar}""",
+            )
 
-        assert (
-            '<div class="sourceline">${&#39;привет&#39; + foobar}</div>'
-            in result_lines(
-                l.get_template("foo.html").render().decode("utf-8")
+        if compat.py3k:
+            assert (
+                '<div class="sourceline">${&#39;привет&#39; + foobar}</div>'
+                in result_lines(
+                    l.get_template("foo.html").render().decode("utf-8")
+                )
+            )
+        else:
+            assert (
+                "${u&#39;&#x43F;&#x440;&#x438;&#x432;&#x435;"
+                "&#x442;&#39; + foobar}"
+                in result_lines(
+                    l.get_template("foo.html").render().decode("utf-8")
+                )
             )
-        )
 
     def test_mod_no_encoding(self):
 
@@ -270,6 +358,10 @@ ${foobar}
         except:
             t, v, tback = sys.exc_info()
 
+        if not compat.py3k:
+            # blow away tracebaack info
+            sys.exc_clear()
+
         # and don't even send what we have.
         html_error = exceptions.html_error_template().render_unicode(
             error=v, traceback=None
@@ -365,8 +457,7 @@ def broken():
             t.render()
         except:
             text_error = exceptions.text_error_template().render_unicode()
-            assert (
-                """
+            assert """
   File "base.html", line 5, in render_body
     %> body starts here
   File "foo.html", line 4, in render_foo
@@ -375,8 +466,6 @@ def broken():
     ${broken()}
   File "base.html", line 4, in broken
     raise RuntimeError("Something went wrong.")
-"""
-                in text_error
-            )
+""" in text_error
         else:
             assert False
diff --git a/third_party/mako/mako/test/test_filters.py b/third_party/mako/mako/test/test_filters.py
index 726f5d74fb64..a58a01f9dd8e 100644
--- a/third_party/mako/mako/test/test_filters.py
+++ b/third_party/mako/mako/test/test_filters.py
@@ -1,8 +1,15 @@
+# -*- coding: utf-8 -*-
+
+import unittest
+
+from mako import compat
+from mako.compat import u
 from mako.template import Template
-from mako.testing.assertions import eq_
-from mako.testing.fixtures import TemplateTest
-from mako.testing.helpers import flatten_result
-from mako.testing.helpers import result_lines
+from test import eq_
+from test import requires_python_2
+from test import TemplateTest
+from test.util import flatten_result
+from test.util import result_lines
 
 
 class FilterTest(TemplateTest):
@@ -70,7 +77,7 @@ class FilterTest(TemplateTest):
         )
 
         eq_(
-            flatten_result(t.render(bar="酒吧bar")),
+            flatten_result(t.render(bar=u"酒吧bar")),
             "http://example.com/?bar=%E9%85%92%E5%90%A7bar&v=1",
         )
 
@@ -81,6 +88,36 @@ class FilterTest(TemplateTest):
             "foo &lt;'some bar'&gt;",
         )
 
+    @requires_python_2
+    def test_quoting_non_unicode(self):
+        t = Template(
+            """
+            foo ${bar | h}
+        """,
+            disable_unicode=True,
+            output_encoding=None,
+        )
+
+        eq_(
+            flatten_result(t.render(bar="<'привет'>")),
+            "foo &lt;&#39;привет&#39;&gt;",
+        )
+
+    @requires_python_2
+    def test_url_escaping_non_unicode(self):
+        t = Template(
+            """
+            http://example.com/?bar=${bar | u}&v=1
+        """,
+            disable_unicode=True,
+            output_encoding=None,
+        )
+
+        eq_(
+            flatten_result(t.render(bar="酒吧bar")),
+            "http://example.com/?bar=%E9%85%92%E5%90%A7bar&v=1",
+        )
+
     def test_def(self):
         t = Template(
             """
@@ -139,8 +176,8 @@ class FilterTest(TemplateTest):
             default_filters=["decode.utf8"],
         )
         eq_(
-            t.render_unicode(x="voix m’a réveillé").strip(),
-            "some stuff.... voix m’a réveillé",
+            t.render_unicode(x=u("voix m’a réveillé")).strip(),
+            u("some stuff.... voix m’a réveillé"),
         )
 
     def test_encode_filter_non_str(self):
@@ -150,7 +187,21 @@ class FilterTest(TemplateTest):
         """,
             default_filters=["decode.utf8"],
         )
-        eq_(t.render_unicode(x=3).strip(), "some stuff.... 3")
+        eq_(t.render_unicode(x=3).strip(), u("some stuff.... 3"))
+
+    @requires_python_2
+    def test_encode_filter_non_str_we_return_bytes(self):
+        class Foo(object):
+            def __str__(self):
+                return compat.b("å")
+
+        t = Template(
+            """# coding: utf-8
+            some stuff.... ${x}
+        """,
+            default_filters=["decode.utf8"],
+        )
+        eq_(t.render_unicode(x=Foo()).strip(), u("some stuff.... å"))
 
     def test_custom_default(self):
         t = Template(
@@ -296,7 +347,7 @@ data = {a: ${123}, b: ${"123"}};
         t = Template(
             """
         <%!
-            class Foo:
+            class Foo(object):
                 foo = True
                 def __str__(self):
                     return "this is a"
@@ -361,7 +412,7 @@ data = {a: ${123}, b: ${"123"}};
         )
 
 
-class BufferTest:
+class BufferTest(unittest.TestCase):
     def test_buffered_def(self):
         t = Template(
             """
diff --git a/third_party/mako/mako/test/test_inheritance.py b/third_party/mako/mako/test/test_inheritance.py
index 15bd54b16e5f..e8ed74e21934 100644
--- a/third_party/mako/mako/test/test_inheritance.py
+++ b/third_party/mako/mako/test/test_inheritance.py
@@ -1,8 +1,11 @@
+import unittest
+
+from mako import compat
 from mako import lookup
-from mako.testing.helpers import result_lines
+from test.util import result_lines
 
 
-class InheritanceTest:
+class InheritanceTest(unittest.TestCase):
     def test_basic(self):
         collection = lookup.TemplateLookup()
 
@@ -254,13 +257,22 @@ ${next.body()}
         """,
         )
 
-        assert result_lines(
-            collection.get_template("index").render_unicode(x=5, y=10)
-        ) == [
-            "this is the base.",
-            "pageargs: (type: <class 'dict'>) [('x', 5), ('y', 10)]",
-            "print 5, 10, 7",
-        ]
+        if compat.py3k:
+            assert result_lines(
+                collection.get_template("index").render_unicode(x=5, y=10)
+            ) == [
+                "this is the base.",
+                "pageargs: (type: <class 'dict'>) [('x', 5), ('y', 10)]",
+                "print 5, 10, 7",
+            ]
+        else:
+            assert result_lines(
+                collection.get_template("index").render_unicode(x=5, y=10)
+            ) == [
+                "this is the base.",
+                "pageargs: (type: <type 'dict'>) [('x', 5), ('y', 10)]",
+                "print 5, 10, 7",
+            ]
 
     def test_pageargs_2(self):
         collection = lookup.TemplateLookup()
diff --git a/third_party/mako/mako/test/test_lexer.py b/third_party/mako/mako/test/test_lexer.py
index 255c128f8de4..9807961ddddb 100644
--- a/third_party/mako/mako/test/test_lexer.py
+++ b/third_party/mako/mako/test/test_lexer.py
@@ -6,11 +6,10 @@ from mako import parsetree
 from mako import util
 from mako.lexer import Lexer
 from mako.template import Template
-from mako.testing.assertions import assert_raises
-from mako.testing.assertions import assert_raises_message
-from mako.testing.assertions import eq_
-from mako.testing.fixtures import TemplateTest
-from mako.testing.helpers import flatten_result
+from test import assert_raises_message
+from test import eq_
+from test import TemplateTest
+from test.util import flatten_result
 
 # create fake parsetree classes which are constructed
 # exactly as the repr() of a real parsetree object.
@@ -26,12 +25,13 @@ def repr_arg(x):
 
 
 def _as_unicode(arg):
-    if isinstance(arg, dict):
-        return {k: _as_unicode(v) for k, v in arg.items()}
+    if isinstance(arg, compat.string_types):
+        return compat.text_type(arg)
+    elif isinstance(arg, dict):
+        return dict((_as_unicode(k), _as_unicode(v)) for k, v in arg.items())
     else:
         return arg
 
-
 Node = None
 TemplateNode = None
 ControlLine = None
@@ -59,7 +59,7 @@ for cls in list(parsetree.__dict__.values()):
         exec(
             (
                 """
-class %s:
+class %s(object):
     def __init__(self, *args):
         self.args = [_as_unicode(arg) for arg in args]
     def __repr__(self):
@@ -138,13 +138,13 @@ class LexerTest(TemplateTest):
 
             hi.
         """
-        assert_raises(exceptions.SyntaxException, Lexer(template).parse)
+        self.assertRaises(exceptions.SyntaxException, Lexer(template).parse)
 
     def test_noexpr_allowed(self):
         template = """
             <%namespace name="${foo}"/>
         """
-        assert_raises(exceptions.CompileException, Lexer(template).parse)
+        self.assertRaises(exceptions.CompileException, Lexer(template).parse)
 
     def test_unmatched_tag(self):
         template = """
@@ -157,13 +157,13 @@ class LexerTest(TemplateTest):
 
         hi.
 """
-        assert_raises(exceptions.SyntaxException, Lexer(template).parse)
+        self.assertRaises(exceptions.SyntaxException, Lexer(template).parse)
 
     def test_nonexistent_tag(self):
         template = """
             <%lala x="5"/>
         """
-        assert_raises(exceptions.CompileException, Lexer(template).parse)
+        self.assertRaises(exceptions.CompileException, Lexer(template).parse)
 
     def test_wrongcase_tag(self):
         template = """
@@ -171,7 +171,7 @@ class LexerTest(TemplateTest):
             </%def>
 
         """
-        assert_raises(exceptions.CompileException, Lexer(template).parse)
+        self.assertRaises(exceptions.CompileException, Lexer(template).parse)
 
     def test_percent_escape(self):
         template = """
@@ -247,7 +247,7 @@ class LexerTest(TemplateTest):
                                 " % more code\n            "
                                 "<%illegal compionent>/></>\n"
                                 '            <%def name="laal()">def</%def>'
-                                "\n\n\n        ",
+                                '\n\n\n        ',
                                 (6, 16),
                             )
                         ],
@@ -274,7 +274,7 @@ class LexerTest(TemplateTest):
             hi
         </%def>
 """
-        assert_raises(exceptions.CompileException, Lexer(template).parse)
+        self.assertRaises(exceptions.CompileException, Lexer(template).parse)
 
     def test_def_syntax_2(self):
         template = """
@@ -282,7 +282,7 @@ class LexerTest(TemplateTest):
             hi
         </%def>
     """
-        assert_raises(exceptions.CompileException, Lexer(template).parse)
+        self.assertRaises(exceptions.CompileException, Lexer(template).parse)
 
     def test_whitespace_equals(self):
         template = """
@@ -515,8 +515,10 @@ class LexerTest(TemplateTest):
             ),
         )
 
-    def test_code(self):
-        template = """text
+    if compat.py3k:
+
+        def test_code(self):
+            template = """text
     <%
         print("hi")
         for x in range(1,5):
@@ -527,25 +529,59 @@ more text
         import foo
     %>
 """
-        nodes = Lexer(template).parse()
-        self._compare(
-            nodes,
-            TemplateNode(
-                {},
-                [
-                    Text("text\n    ", (1, 1)),
-                    Code(
-                        '\nprint("hi")\nfor x in range(1,5):\n    '
-                        "print(x)\n    \n",
-                        False,
-                        (2, 5),
-                    ),
-                    Text("\nmore text\n    ", (6, 7)),
-                    Code("\nimport foo\n    \n", True, (8, 5)),
-                    Text("\n", (10, 7)),
-                ],
-            ),
-        )
+            nodes = Lexer(template).parse()
+            self._compare(
+                nodes,
+                TemplateNode(
+                    {},
+                    [
+                        Text("text\n    ", (1, 1)),
+                        Code(
+                            '\nprint("hi")\nfor x in range(1,5):\n    '
+                            "print(x)\n    \n",
+                            False,
+                            (2, 5),
+                        ),
+                        Text("\nmore text\n    ", (6, 7)),
+                        Code("\nimport foo\n    \n", True, (8, 5)),
+                        Text("\n", (10, 7)),
+                    ],
+                ),
+            )
+
+    else:
+
+        def test_code(self):
+            template = """text
+    <%
+        print "hi"
+        for x in range(1,5):
+            print x
+    %>
+more text
+    <%!
+        import foo
+    %>
+"""
+            nodes = Lexer(template).parse()
+            self._compare(
+                nodes,
+                TemplateNode(
+                    {},
+                    [
+                        Text("text\n    ", (1, 1)),
+                        Code(
+                            '\nprint "hi"\nfor x in range(1,5):\n    '
+                            "print x\n    \n",
+                            False,
+                            (2, 5),
+                        ),
+                        Text("\nmore text\n    ", (6, 7)),
+                        Code("\nimport foo\n    \n", True, (8, 5)),
+                        Text("\n", (10, 7)),
+                    ],
+                ),
+            )
 
     def test_code_and_tags(self):
         template = """
@@ -705,11 +741,20 @@ more text
         )
 
     def test_tricky_code(self):
-        template = """<% print('hi %>') %>"""
-        nodes = Lexer(template).parse()
-        self._compare(
-            nodes, TemplateNode({}, [Code("print('hi %>') \n", False, (1, 1))])
-        )
+        if compat.py3k:
+            template = """<% print('hi %>') %>"""
+            nodes = Lexer(template).parse()
+            self._compare(
+                nodes,
+                TemplateNode({}, [Code("print('hi %>') \n", False, (1, 1))]),
+            )
+        else:
+            template = """<% print 'hi %>' %>"""
+            nodes = Lexer(template).parse()
+            self._compare(
+                nodes,
+                TemplateNode({}, [Code("print 'hi %>' \n", False, (1, 1))]),
+            )
 
     def test_tricky_code_2(self):
         template = """<%
@@ -735,42 +780,77 @@ more text
             ),
         )
 
-    def test_tricky_code_3(self):
-        template = """<%
-        print('hi')
-        # this is a comment
-        # another comment
-        x = 7 # someone's '''comment
-        print('''
-    there
-    ''')
-        # someone else's comment
+    if compat.py3k:
+
+        def test_tricky_code_3(self):
+            template = """<%
+            print('hi')
+            # this is a comment
+            # another comment
+            x = 7 # someone's '''comment
+            print('''
+        there
+        ''')
+            # someone else's comment
 %> '''and now some text '''"""
-        nodes = Lexer(template).parse()
-        self._compare(
-            nodes,
-            TemplateNode(
-                {},
-                [
-                    Code(
-                        """
+            nodes = Lexer(template).parse()
+            self._compare(
+                nodes,
+                TemplateNode(
+                    {},
+                    [
+                        Code(
+                            """
 print('hi')
 # this is a comment
 # another comment
 x = 7 # someone's '''comment
 print('''
-    there
-    ''')
+        there
+        ''')
 # someone else's comment
 
 """,
-                        False,
-                        (1, 1),
-                    ),
-                    Text(" '''and now some text '''", (10, 3)),
-                ],
-            ),
-        )
+                            False,
+                            (1, 1),
+                        ),
+                        Text(" '''and now some text '''", (10, 3)),
+                    ],
+                ),
+            )
+
+    else:
+
+        def test_tricky_code_3(self):
+            template = """<%
+            print 'hi'
+            # this is a comment
+            # another comment
+            x = 7 # someone's '''comment
+            print '''
+        there
+        '''
+            # someone else's comment
+%> '''and now some text '''"""
+            nodes = Lexer(template).parse()
+            self._compare(
+                nodes,
+                TemplateNode(
+                    {},
+                    [
+                        Code(
+                            """\nprint 'hi'\n# this is a comment\n"""
+                            """# another comment\nx = 7 """
+                            """# someone's '''comment\nprint '''\n        """
+                            """there\n        '''\n# someone else's """
+                            """comment\n\n""",
+                            False,
+                            (1, 1),
+                        ),
+                        Text(" '''and now some text '''", (10, 3)),
+                    ],
+                ),
+            )
 
     def test_tricky_code_4(self):
         template = """<% foo = "\\"\\\\" %>"""
diff --git a/third_party/mako/mako/test/test_lookup.py b/third_party/mako/mako/test/test_lookup.py
index 6a797d7a4b6f..0dacfbb75220 100644
--- a/third_party/mako/mako/test/test_lookup.py
+++ b/third_party/mako/mako/test/test_lookup.py
@@ -1,23 +1,21 @@
 import os
-import tempfile
+import unittest
 
+from mako import compat
 from mako import exceptions
 from mako import lookup
 from mako import runtime
 from mako.template import Template
-from mako.testing.assertions import assert_raises_message
-from mako.testing.assertions import assert_raises_with_given_cause
-from mako.testing.config import config
-from mako.testing.helpers import file_with_template_code
-from mako.testing.helpers import replace_file_with_dir
-from mako.testing.helpers import result_lines
-from mako.testing.helpers import rewind_compile_time
 from mako.util import FastEncodingBuffer
+from test import assert_raises_message
+from test import eq_
+from test import template_base
+from test.util import result_lines
 
-tl = lookup.TemplateLookup(directories=[config.template_base])
+tl = lookup.TemplateLookup(directories=[template_base])
 
 
-class LookupTest:
+class LookupTest(unittest.TestCase):
     def test_basic(self):
         t = tl.get_template("index.html")
         assert result_lines(t.render()) == ["this is index"]
@@ -45,22 +43,21 @@ class LookupTest:
         """test that hitting an existent directory still raises
         LookupError."""
 
-        assert_raises_with_given_cause(
-            exceptions.TopLevelLookupException,
-            KeyError,
-            tl.get_template,
-            "/subdir",
+        self.assertRaises(
+            exceptions.TopLevelLookupException, tl.get_template, "/subdir"
         )
 
     def test_no_lookup(self):
         t = Template("hi <%include file='foo.html'/>")
-
-        assert_raises_message(
-            exceptions.TemplateLookupException,
-            "Template 'memory:%s' has no TemplateLookup associated"
-            % hex(id(t)),
-            t.render,
-        )
+        try:
+            t.render()
+            assert False
+        except exceptions.TemplateLookupException:
+            eq_(
+                str(compat.exception_as()),
+                "Template 'memory:%s' has no TemplateLookup associated"
+                % hex(id(t)),
+            )
 
     def test_uri_adjust(self):
         tl = lookup.TemplateLookup(directories=["/foo/bar"])
@@ -85,11 +82,8 @@ class LookupTest:
         f = tl.get_template("foo")
         assert f.uri in tl._collection
         f.filename = "nonexistent"
-        assert_raises_with_given_cause(
-            exceptions.TemplateLookupException,
-            FileNotFoundError,
-            tl.get_template,
-            "foo",
+        self.assertRaises(
+            exceptions.TemplateLookupException, tl.get_template, "foo"
         )
         assert f.uri not in tl._collection
 
@@ -97,7 +91,7 @@ class LookupTest:
         """test the mechanics of an include where
         the include goes outside of the path"""
         tl = lookup.TemplateLookup(
-            directories=[os.path.join(config.template_base, "subdir")]
+            directories=[os.path.join(template_base, "subdir")]
         )
         index = tl.get_template("index.html")
 
@@ -126,25 +120,3 @@ class LookupTest:
 
         # this is OK since the .. cancels out
         runtime._lookup_template(ctx, "foo/../index.html", index.uri)
-
-    def test_checking_against_bad_filetype(self):
-        with tempfile.TemporaryDirectory() as tempdir:
-            tl = lookup.TemplateLookup(directories=[tempdir])
-            index_file = file_with_template_code(
-                os.path.join(tempdir, "index.html")
-            )
-
-            with rewind_compile_time():
-                tmpl = Template(filename=index_file)
-
-            tl.put_template("index.html", tmpl)
-
-            replace_file_with_dir(index_file)
-
-            assert_raises_with_given_cause(
-                exceptions.TemplateLookupException,
-                OSError,
-                tl._check,
-                "index.html",
-                tl._collection["index.html"],
-            )
diff --git a/third_party/mako/mako/test/test_loop.py b/third_party/mako/mako/test/test_loop.py
index 19d40b542d36..8104e49cfb6b 100644
--- a/third_party/mako/mako/test/test_loop.py
+++ b/third_party/mako/mako/test/test_loop.py
@@ -1,4 +1,5 @@
 import re
+import unittest
 
 from mako import exceptions
 from mako.codegen import _FOR_LOOP
@@ -6,12 +7,12 @@ from mako.lookup import TemplateLookup
 from mako.runtime import LoopContext
 from mako.runtime import LoopStack
 from mako.template import Template
-from mako.testing.assertions import assert_raises_message
-from mako.testing.fixtures import TemplateTest
-from mako.testing.helpers import flatten_result
+from test import assert_raises_message
+from test import TemplateTest
+from test.util import flatten_result
 
 
-class TestLoop:
+class TestLoop(unittest.TestCase):
     def test__FOR_LOOP(self):
         for statement, target_list, expression_list in (
             ("for x in y:", "x", "y"),
@@ -136,7 +137,7 @@ ${x} ${loop.index} <- outer loop
         )
 
 
-class TestLoopStack:
+class TestLoopStack(unittest.TestCase):
     def setUp(self):
         self.stack = LoopStack()
         self.bottom = "spam"
@@ -179,7 +180,7 @@ class TestLoopStack:
         assert before == (after + 1), "Exiting a context pops the stack"
 
 
-class TestLoopContext:
+class TestLoopContext(unittest.TestCase):
     def setUp(self):
         self.iterable = [1, 2, 3]
         self.ctx = LoopContext(self.iterable)
@@ -198,7 +199,7 @@ class TestLoopContext:
 
     def test_reverse_index(self):
         length = len(self.iterable)
-        expected = tuple(length - i - 1 for i in range(length))
+        expected = tuple([length - i - 1 for i in range(length)])
         actual = tuple(self.ctx.reverse_index for i in self.ctx)
         print(expected, actual)
         assert expected == actual, (
diff --git a/third_party/mako/mako/test/test_lru.py b/third_party/mako/mako/test/test_lru.py
index f54bd1580f5b..728153798e03 100644
--- a/third_party/mako/mako/test/test_lru.py
+++ b/third_party/mako/mako/test/test_lru.py
@@ -1,3 +1,5 @@
+import unittest
+
 from mako.util import LRUCache
 
 
@@ -9,7 +11,7 @@ class item:
         return "item id %d" % self.id
 
 
-class LRUTest:
+class LRUTest(unittest.TestCase):
     def testlru(self):
         l = LRUCache(10, threshold=0.2)
 
diff --git a/third_party/mako/mako/test/test_namespace.py b/third_party/mako/mako/test/test_namespace.py
index b6b0544f1a60..8d223d54ed47 100644
--- a/third_party/mako/mako/test/test_namespace.py
+++ b/third_party/mako/mako/test/test_namespace.py
@@ -1,12 +1,9 @@
-from mako import exceptions
 from mako import lookup
 from mako.template import Template
-from mako.testing.assertions import assert_raises
-from mako.testing.assertions import assert_raises_message_with_given_cause
-from mako.testing.assertions import eq_
-from mako.testing.fixtures import TemplateTest
-from mako.testing.helpers import flatten_result
-from mako.testing.helpers import result_lines
+from test import eq_
+from test import TemplateTest
+from test.util import flatten_result
+from test.util import result_lines
 
 
 class NamespaceTest(TemplateTest):
@@ -587,7 +584,7 @@ class NamespaceTest(TemplateTest):
         """,
         )
 
-        assert_raises(AttributeError, l.get_template("bar.html").render)
+        self.assertRaises(AttributeError, l.get_template("bar.html").render)
 
     def test_custom_tag_1(self):
         template = Template(
@@ -997,35 +994,3 @@ class NamespaceTest(TemplateTest):
             "this is lala",
             "this is foo",
         ]
-
-    def test_nonexistent_namespace_uri(self):
-        collection = lookup.TemplateLookup()
-        collection.put_string(
-            "main.html",
-            """
-            <%namespace name="defs" file="eefs.html"/>
-
-            this is main.  ${defs.def1("hi")}
-            ${defs.def2("there")}
-""",
-        )
-
-        collection.put_string(
-            "defs.html",
-            """
-        <%def name="def1(s)">
-            def1: ${s}
-        </%def>
-
-        <%def name="def2(x)">
-            def2: ${x}
-        </%def>
-""",
-        )
-
-        assert_raises_message_with_given_cause(
-            exceptions.TemplateLookupException,
-            "Can't locate template for uri 'eefs.html",
-            exceptions.TopLevelLookupException,
-            collection.get_template("main.html").render,
-        )
diff --git a/third_party/mako/mako/test/test_pygen.py b/third_party/mako/mako/test/test_pygen.py
index 8adc14263b40..daa76dea92fd 100644
--- a/third_party/mako/mako/test/test_pygen.py
+++ b/third_party/mako/mako/test/test_pygen.py
@@ -1,11 +1,12 @@
-from io import StringIO
+import unittest
 
+from mako.compat import StringIO
 from mako.pygen import adjust_whitespace
 from mako.pygen import PythonPrinter
-from mako.testing.assertions import eq_
+from test import eq_
 
 
-class GeneratePythonTest:
+class GeneratePythonTest(unittest.TestCase):
     def test_generate_normal(self):
         stream = StringIO()
         printer = PythonPrinter(stream)
@@ -164,7 +165,7 @@ print "more indent"
         )
 
 
-class WhitespaceTest:
+class WhitespaceTest(unittest.TestCase):
     def test_basic(self):
         text = """
         for x in range(0,15):
diff --git a/third_party/mako/mako/test/test_runtime.py b/third_party/mako/mako/test/test_runtime.py
index 0d6fce37917f..d87d264a3e7f 100644
--- a/third_party/mako/mako/test/test_runtime.py
+++ b/third_party/mako/mako/test/test_runtime.py
@@ -1,10 +1,12 @@
 """Assorted runtime unit tests
 """
+import unittest
+
 from mako import runtime
-from mako.testing.assertions import eq_
+from test import eq_
 
 
-class ContextTest:
+class ContextTest(unittest.TestCase):
     def test_locals_kwargs(self):
         c = runtime.Context(None, foo="bar")
         eq_(c.kwargs, {"foo": "bar"})
diff --git a/third_party/mako/mako/test/test_template.py b/third_party/mako/mako/test/test_template.py
index fc1aecad5bfb..40fd10cc927e 100644
--- a/third_party/mako/mako/test/test_template.py
+++ b/third_party/mako/mako/test/test_template.py
@@ -1,23 +1,30 @@
+# -*- coding: utf-8 -*-
+
 import os
+import unittest
 
+from mako import compat
 from mako import exceptions
 from mako import runtime
 from mako import util
+from mako.compat import u
 from mako.ext.preprocessors import convert_comments
 from mako.lookup import TemplateLookup
 from mako.template import ModuleInfo
 from mako.template import ModuleTemplate
 from mako.template import Template
-from mako.testing.assertions import assert_raises
-from mako.testing.assertions import assert_raises_message
-from mako.testing.assertions import eq_
-from mako.testing.config import config
-from mako.testing.fixtures import TemplateTest
-from mako.testing.helpers import flatten_result
-from mako.testing.helpers import result_lines
-
-
-class ctx:
+from test import assert_raises
+from test import assert_raises_message
+from test import eq_
+from test import module_base
+from test import requires_python_2
+from test import template_base
+from test import TemplateTest
+from test.util import flatten_result
+from test.util import result_lines
+
+
+class ctx(object):
     def __init__(self, a, b):
         pass
 
@@ -28,23 +35,6 @@ class ctx:
         pass
 
 
-class MiscTest(TemplateTest):
-    def test_crlf_linebreaks(self):
-
-        crlf = r"""
-<%
-    foo = True
-    bar = True
-%>
-% if foo and \
-     bar:
-     foo and bar
-%endif
-"""
-        crlf = crlf.replace("\n", "\r\n")
-        self._do_test(Template(crlf), "\r\n\r\n     foo and bar\r\n")
-
-
 class EncodingTest(TemplateTest):
     def test_escapes_html_tags(self):
         from mako.exceptions import html_error_template
@@ -61,23 +51,34 @@ class EncodingTest(TemplateTest):
         except:
             # <h3>Exception: <span style="color:red">Foobar</span></h3>
             markup = html_error_template().render(full=False, css=False)
-            assert (
-                '<span style="color:red">Foobar</span></h3>'.encode("ascii")
-                not in markup
-            )
-            assert (
-                "&lt;span style=&#34;color:red&#34;"
-                "&gt;Foobar&lt;/span&gt;".encode("ascii") in markup
-            )
+            if compat.py3k:
+                assert (
+                    '<span style="color:red">Foobar</span></h3>'.encode(
+                        "ascii"
+                    )
+                    not in markup
+                )
+                assert (
+                    "&lt;span style=&#34;color:red&#34;"
+                    "&gt;Foobar&lt;/span&gt;".encode("ascii") in markup
+                )
+            else:
+                assert (
+                    '<span style="color:red">Foobar</span></h3>' not in markup
+                )
+                assert (
+                    "&lt;span style=&#34;color:red&#34;"
+                    "&gt;Foobar&lt;/span&gt;" in markup
+                )
 
     def test_unicode(self):
         self._do_memory_test(
-            (
+            u(
                 "Alors vous imaginez ma surprise, au lever du jour, quand "
                 "une drôle de petite voix m’a réveillé. Elle disait: "
                 "« S’il vous plaît… dessine-moi un mouton! »"
             ),
-            (
+            u(
                 "Alors vous imaginez ma surprise, au lever du jour, quand "
                 "une drôle de petite voix m’a réveillé. Elle disait: "
                 "« S’il vous plaît… dessine-moi un mouton! »"
@@ -86,12 +87,12 @@ class EncodingTest(TemplateTest):
 
     def test_encoding_doesnt_conflict(self):
         self._do_memory_test(
-            (
+            u(
                 "Alors vous imaginez ma surprise, au lever du jour, quand "
                 "une drôle de petite voix m’a réveillé. Elle disait: "
                 "« S’il vous plaît… dessine-moi un mouton! »"
             ),
-            (
+            u(
                 "Alors vous imaginez ma surprise, au lever du jour, quand "
                 "une drôle de petite voix m’a réveillé. Elle disait: "
                 "« S’il vous plaît… dessine-moi un mouton! »"
@@ -100,14 +101,14 @@ class EncodingTest(TemplateTest):
         )
 
     def test_unicode_arg(self):
-        val = (
+        val = u(
             "Alors vous imaginez ma surprise, au lever du jour, quand "
             "une drôle de petite voix m’a réveillé. Elle disait: "
             "« S’il vous plaît… dessine-moi un mouton! »"
         )
         self._do_memory_test(
             "${val}",
-            (
+            u(
                 "Alors vous imaginez ma surprise, au lever du jour, quand "
                 "une drôle de petite voix m’a réveillé. Elle disait: "
                 "« S’il vous plaît… dessine-moi un mouton! »"
@@ -118,7 +119,7 @@ class EncodingTest(TemplateTest):
     def test_unicode_file(self):
         self._do_file_test(
             "unicode.html",
-            (
+            u(
                 "Alors vous imaginez ma surprise, au lever du jour, quand "
                 "une drôle de petite voix m’a réveillé. Elle disait: "
                 "« S’il vous plaît… dessine-moi un mouton! »"
@@ -128,26 +129,29 @@ class EncodingTest(TemplateTest):
     def test_unicode_file_code(self):
         self._do_file_test(
             "unicode_code.html",
-            ("""hi, drôle de petite voix m’a réveillé."""),
+            u("""hi, drôle de petite voix m’a réveillé."""),
             filters=flatten_result,
         )
 
     def test_unicode_file_lookup(self):
         lookup = TemplateLookup(
-            directories=[config.template_base],
+            directories=[template_base],
             output_encoding="utf-8",
             default_filters=["decode.utf8"],
         )
-        template = lookup.get_template("/chs_unicode_py3k.html")
+        if compat.py3k:
+            template = lookup.get_template("/chs_unicode_py3k.html")
+        else:
+            template = lookup.get_template("/chs_unicode.html")
         eq_(
             flatten_result(template.render_unicode(name="毛泽东")),
-            ("毛泽东 是 新中国的主席<br/> Welcome 你 to 北京."),
+            u("毛泽东 是 新中国的主席<br/> Welcome 你 to 北京."),
         )
 
     def test_unicode_bom(self):
         self._do_file_test(
             "bom.html",
-            (
+            u(
                 "Alors vous imaginez ma surprise, au lever du jour, quand "
                 "une drôle de petite voix m’a réveillé. Elle disait: "
                 "« S’il vous plaît… dessine-moi un mouton! »"
@@ -156,29 +160,29 @@ class EncodingTest(TemplateTest):
 
         self._do_file_test(
             "bommagic.html",
-            (
+            u(
                 "Alors vous imaginez ma surprise, au lever du jour, quand "
                 "une drôle de petite voix m’a réveillé. Elle disait: "
                 "« S’il vous plaît… dessine-moi un mouton! »"
             ),
         )
 
-        assert_raises(
+        self.assertRaises(
             exceptions.CompileException,
             Template,
             filename=self._file_path("badbom.html"),
-            module_directory=config.module_base,
+            module_directory=module_base,
         )
 
     def test_unicode_memory(self):
-        val = (
+        val = u(
             "Alors vous imaginez ma surprise, au lever du jour, quand "
             "une drôle de petite voix m’a réveillé. Elle disait: "
             "« S’il vous plaît… dessine-moi un mouton! »"
         )
         self._do_memory_test(
             ("## -*- coding: utf-8 -*-\n" + val).encode("utf-8"),
-            (
+            u(
                 "Alors vous imaginez ma surprise, au lever du jour, quand "
                 "une drôle de petite voix m’a réveillé. Elle disait: "
                 "« S’il vous plaît… dessine-moi un mouton! »"
@@ -186,14 +190,14 @@ class EncodingTest(TemplateTest):
         )
 
     def test_unicode_text(self):
-        val = (
+        val = u(
             "<%text>Alors vous imaginez ma surprise, au lever du jour, quand "
             "une drôle de petite voix m’a réveillé. Elle disait: "
             "« S’il vous plaît… dessine-moi un mouton! »</%text>"
         )
         self._do_memory_test(
             ("## -*- coding: utf-8 -*-\n" + val).encode("utf-8"),
-            (
+            u(
                 "Alors vous imaginez ma surprise, au lever du jour, quand "
                 "une drôle de petite voix m’a réveillé. Elle disait: "
                 "« S’il vous plaît… dessine-moi un mouton! »"
@@ -201,7 +205,8 @@ class EncodingTest(TemplateTest):
         )
 
     def test_unicode_text_ccall(self):
-        val = """
+        val = u(
+            """
         <%def name="foo()">
             ${capture(caller.body)}
         </%def>
@@ -210,9 +215,10 @@ class EncodingTest(TemplateTest):
 quand une drôle de petite voix m’a réveillé. Elle disait:
 « S’il vous plaît… dessine-moi un mouton! »</%text>
         </%call>"""
+        )
         self._do_memory_test(
             ("## -*- coding: utf-8 -*-\n" + val).encode("utf-8"),
-            (
+            u(
                 "Alors vous imaginez ma surprise, au lever du jour, quand "
                 "une drôle de petite voix m’a réveillé. Elle disait: "
                 "« S’il vous plaît… dessine-moi un mouton! »"
@@ -221,26 +227,43 @@ quand une drôle de petite voix m’a réveillé. Elle disait:
         )
 
     def test_unicode_literal_in_expr(self):
-        self._do_memory_test(
-            (
-                "## -*- coding: utf-8 -*-\n"
-                '${"Alors vous imaginez ma surprise, au lever du jour, '
-                "quand une drôle de petite voix m’a réveillé. "
-                "Elle disait: "
-                '« S’il vous plaît… dessine-moi un mouton! »"}\n'
-            ).encode("utf-8"),
-            (
-                "Alors vous imaginez ma surprise, au lever du jour, "
-                "quand une drôle de petite voix m’a réveillé. "
-                "Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
-            ),
-            filters=lambda s: s.strip(),
-        )
+        if compat.py3k:
+            self._do_memory_test(
+                u(
+                    "## -*- coding: utf-8 -*-\n"
+                    '${"Alors vous imaginez ma surprise, au lever du jour, '
+                    "quand une drôle de petite voix m’a réveillé. "
+                    "Elle disait: "
+                    '« S’il vous plaît… dessine-moi un mouton! »"}\n'
+                ).encode("utf-8"),
+                u(
+                    "Alors vous imaginez ma surprise, au lever du jour, "
+                    "quand une drôle de petite voix m’a réveillé. "
+                    "Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
+                ),
+                filters=lambda s: s.strip(),
+            )
+        else:
+            self._do_memory_test(
+                u(
+                    "## -*- coding: utf-8 -*-\n"
+                    '${u"Alors vous imaginez ma surprise, au lever du jour, '
+                    "quand une drôle de petite voix m’a réveillé. "
+                    "Elle disait: « S’il vous plaît… dessine-moi un "
+                    'mouton! »"}'
+                ).encode("utf-8"),
+                u(
+                    "Alors vous imaginez ma surprise, au lever du jour, "
+                    "quand une drôle de petite voix m’a réveillé. "
+                    "Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
+                ),
+                filters=lambda s: s.strip(),
+            )
 
     def test_unicode_literal_in_expr_file(self):
         self._do_file_test(
             "unicode_expr.html",
-            (
+            u(
                 "Alors vous imaginez ma surprise, au lever du jour, "
                 "quand une drôle de petite voix m’a réveillé. "
                 "Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
@@ -249,49 +272,85 @@ quand une drôle de petite voix m’a réveillé. Elle disait:
         )
 
     def test_unicode_literal_in_code(self):
-        self._do_memory_test(
-            (
-                """## -*- coding: utf-8 -*-
-            <%
-                context.write("Alors vous imaginez ma surprise, au """
-                """lever du jour, quand une drôle de petite voix m’a """
-                """réveillé. Elle disait: """
-                """« S’il vous plaît… dessine-moi un mouton! »")
-            %>
-            """
-            ).encode("utf-8"),
-            (
-                "Alors vous imaginez ma surprise, au lever du jour, "
-                "quand une drôle de petite voix m’a réveillé. "
-                "Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
-            ),
-            filters=lambda s: s.strip(),
-        )
+        if compat.py3k:
+            self._do_memory_test(
+                u(
+                    """## -*- coding: utf-8 -*-
+                <%
+                    context.write("Alors vous imaginez ma surprise, au """
+                    """lever du jour, quand une drôle de petite voix m’a """
+                    """réveillé. Elle disait: """
+                    """« S’il vous plaît… dessine-moi un mouton! »")
+                %>
+                """
+                ).encode("utf-8"),
+                u(
+                    "Alors vous imaginez ma surprise, au lever du jour, "
+                    "quand une drôle de petite voix m’a réveillé. "
+                    "Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
+                ),
+                filters=lambda s: s.strip(),
+            )
+        else:
+            self._do_memory_test(
+                u(
+                    """## -*- coding: utf-8 -*-
+                <%
+                    context.write(u"Alors vous imaginez ma surprise, """
+                    """au lever du jour, quand une drôle de petite voix """
+                    """m’a réveillé. Elle disait: « S’il vous plaît… """
+                    """dessine-moi un mouton! »")
+                %>
+                """
+                ).encode("utf-8"),
+                u(
+                    "Alors vous imaginez ma surprise, au lever du jour, "
+                    "quand une drôle de petite voix m’a réveillé. "
+                    "Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
+                ),
+                filters=lambda s: s.strip(),
+            )
 
     def test_unicode_literal_in_controlline(self):
-        self._do_memory_test(
-            (
-                """## -*- coding: utf-8 -*-
-            <%
-                x = "drôle de petite voix m’a réveillé."
-            %>
-            % if x=="drôle de petite voix m’a réveillé.":
-                hi, ${x}
-            % endif
-            """
-            ).encode("utf-8"),
-            ("""hi, drôle de petite voix m’a réveillé."""),
-            filters=lambda s: s.strip(),
-        )
+        if compat.py3k:
+            self._do_memory_test(
+                u(
+                    """## -*- coding: utf-8 -*-
+                <%
+                    x = "drôle de petite voix m’a réveillé."
+                %>
+                % if x=="drôle de petite voix m’a réveillé.":
+                    hi, ${x}
+                % endif
+                """
+                ).encode("utf-8"),
+                u("""hi, drôle de petite voix m’a réveillé."""),
+                filters=lambda s: s.strip(),
+            )
+        else:
+            self._do_memory_test(
+                u(
+                    """## -*- coding: utf-8 -*-
+                <%
+                    x = u"drôle de petite voix m’a réveillé."
+                %>
+                % if x==u"drôle de petite voix m’a réveillé.":
+                    hi, ${x}
+                % endif
+                """
+                ).encode("utf-8"),
+                u("""hi, drôle de petite voix m’a réveillé."""),
+                filters=lambda s: s.strip(),
+            )
 
     def test_unicode_literal_in_tag(self):
         self._do_file_test(
             "unicode_arguments.html",
             [
-                ("x is: drôle de petite voix m’a réveillé"),
-                ("x is: drôle de petite voix m’a réveillé"),
-                ("x is: drôle de petite voix m’a réveillé"),
-                ("x is: drôle de petite voix m’a réveillé"),
+                u("x is: drôle de petite voix m’a réveillé"),
+                u("x is: drôle de petite voix m’a réveillé"),
+                u("x is: drôle de petite voix m’a réveillé"),
+                u("x is: drôle de petite voix m’a réveillé"),
             ],
             filters=result_lines,
         )
@@ -299,75 +358,148 @@ quand une drôle de petite voix m’a réveillé. Elle disait:
         self._do_memory_test(
             util.read_file(self._file_path("unicode_arguments.html")),
             [
-                ("x is: drôle de petite voix m’a réveillé"),
-                ("x is: drôle de petite voix m’a réveillé"),
-                ("x is: drôle de petite voix m’a réveillé"),
-                ("x is: drôle de petite voix m’a réveillé"),
+                u("x is: drôle de petite voix m’a réveillé"),
+                u("x is: drôle de petite voix m’a réveillé"),
+                u("x is: drôle de petite voix m’a réveillé"),
+                u("x is: drôle de petite voix m’a réveillé"),
             ],
             filters=result_lines,
         )
 
     def test_unicode_literal_in_def(self):
-        self._do_memory_test(
-            (
-                """## -*- coding: utf-8 -*-
-            <%def name="bello(foo, bar)">
-            Foo: ${ foo }
-            Bar: ${ bar }
-            </%def>
-            <%call expr="bello(foo='árvíztűrő tükörfúrógép', """
-                """bar='ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP')">
-            </%call>"""
-            ).encode("utf-8"),
-            (
-                """Foo: árvíztűrő tükörfúrógép """
-                """Bar: ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"""
-            ),
-            filters=flatten_result,
-        )
+        if compat.py3k:
+            self._do_memory_test(
+                u(
+                    """## -*- coding: utf-8 -*-
+                <%def name="bello(foo, bar)">
+                Foo: ${ foo }
+                Bar: ${ bar }
+                </%def>
+                <%call expr="bello(foo='árvíztűrő tükörfúrógép', """
+                    """bar='ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP')">
+                </%call>"""
+                ).encode("utf-8"),
+                u(
+                    """Foo: árvíztűrő tükörfúrógép """
+                    """Bar: ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"""
+                ),
+                filters=flatten_result,
+            )
 
-        self._do_memory_test(
-            (
-                "## -*- coding: utf-8 -*-\n"
-                """<%def name="hello(foo='árvíztűrő tükörfúrógép', """
-                """bar='ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP')">\n"""
-                "Foo: ${ foo }\n"
-                "Bar: ${ bar }\n"
-                "</%def>\n"
-                "${ hello() }"
-            ).encode("utf-8"),
-            (
-                """Foo: árvíztűrő tükörfúrógép Bar: """
-                """ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"""
-            ),
-            filters=flatten_result,
-        )
+            self._do_memory_test(
+                u(
+                    "## -*- coding: utf-8 -*-\n"
+                    """<%def name="hello(foo='árvíztűrő tükörfúrógép', """
+                    """bar='ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP')">\n"""
+                    "Foo: ${ foo }\n"
+                    "Bar: ${ bar }\n"
+                    "</%def>\n"
+                    "${ hello() }"
+                ).encode("utf-8"),
+                u(
+                    """Foo: árvíztűrő tükörfúrógép Bar: """
+                    """ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"""
+                ),
+                filters=flatten_result,
+            )
+        else:
+            self._do_memory_test(
+                u(
+                    """## -*- coding: utf-8 -*-
+                <%def name="bello(foo, bar)">
+                Foo: ${ foo }
+                Bar: ${ bar }
+                </%def>
+                <%call expr="bello(foo=u'árvíztűrő tükörfúrógép', """
+                    """bar=u'ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP')">
+                </%call>"""
+                ).encode("utf-8"),
+                u(
+                    """Foo: árvíztűrő tükörfúrógép Bar: """
+                    """ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"""
+                ),
+                filters=flatten_result,
+            )
+
+            self._do_memory_test(
+                u(
+                    """## -*- coding: utf-8 -*-
+                <%def name="hello(foo=u'árvíztűrő tükörfúrógép', """
+                    """bar=u'ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP')">
+                Foo: ${ foo }
+                Bar: ${ bar }
+                </%def>
+                ${ hello() }"""
+                ).encode("utf-8"),
+                u(
+                    """Foo: árvíztűrő tükörfúrógép Bar: """
+                    """ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"""
+                ),
+                filters=flatten_result,
+            )
 
     def test_input_encoding(self):
         """test the 'input_encoding' flag on Template, and that unicode
-        objects arent double-decoded"""
+            objects arent double-decoded"""
+
+        if compat.py3k:
+            self._do_memory_test(
+                u("hello ${f('śląsk')}"),
+                u("hello śląsk"),
+                input_encoding="utf-8",
+                template_args={"f": lambda x: x},
+            )
+
+            self._do_memory_test(
+                u("## -*- coding: utf-8 -*-\nhello ${f('śląsk')}"),
+                u("hello śląsk"),
+                template_args={"f": lambda x: x},
+            )
+        else:
+            self._do_memory_test(
+                u("hello ${f(u'śląsk')}"),
+                u("hello śląsk"),
+                input_encoding="utf-8",
+                template_args={"f": lambda x: x},
+            )
+
+            self._do_memory_test(
+                u("## -*- coding: utf-8 -*-\nhello ${f(u'śląsk')}"),
+                u("hello śląsk"),
+                template_args={"f": lambda x: x},
+            )
+
+    def test_raw_strings(self):
+        """test that raw strings go straight thru with default_filters
+        turned off, bytestring_passthrough enabled.
+
+        """
 
         self._do_memory_test(
-            ("hello ${f('śląsk')}"),
-            ("hello śląsk"),
-            input_encoding="utf-8",
-            template_args={"f": lambda x: x},
+            u("## -*- coding: utf-8 -*-\nhello ${x}"),
+            "hello śląsk",
+            default_filters=[],
+            template_args={"x": "śląsk"},
+            unicode_=False,
+            bytestring_passthrough=True,
+            output_encoding=None,  # 'ascii'
         )
 
+        # now, the way you *should* be doing it....
         self._do_memory_test(
-            ("## -*- coding: utf-8 -*-\nhello ${f('śląsk')}"),
-            ("hello śląsk"),
-            template_args={"f": lambda x: x},
+            u("## -*- coding: utf-8 -*-\nhello ${x}"),
+            u("hello śląsk"),
+            template_args={"x": u("śląsk")},
         )
 
     def test_encoding(self):
         self._do_memory_test(
-            (
+            u(
                 "Alors vous imaginez ma surprise, au lever du jour, quand "
                 "une drôle de petite voix m’a réveillé. Elle disait: "
                 "« S’il vous plaît… dessine-moi un mouton! »"
             ),
-            (
+            u(
                 "Alors vous imaginez ma surprise, au lever du jour, quand "
                 "une drôle de petite voix m’a réveillé. Elle disait: "
                 "« S’il vous plaît… dessine-moi un mouton! »"
@@ -378,13 +510,13 @@ quand une drôle de petite voix m’a réveillé. Elle disait:
 
     def test_encoding_errors(self):
         self._do_memory_test(
-            (
+            u(
                 """KGB (transliteration of "КГБ") is the Russian-language """
                 """abbreviation for Committee for State Security, """
                 """(Russian: Комит́ет Госуд́арственной Безоп́асности """
                 """(help·info); Komitet Gosudarstvennoy Bezopasnosti)"""
             ),
-            (
+            u(
                 """KGB (transliteration of "КГБ") is the Russian-language """
                 """abbreviation for Committee for State Security, """
                 """(Russian: Комит́ет Госуд́арственной Безоп́асности """
@@ -397,17 +529,79 @@ quand une drôle de petite voix m’a réveillé. Elle disait:
 
     def test_read_unicode(self):
         lookup = TemplateLookup(
-            directories=[config.template_base],
+            directories=[template_base],
             filesystem_checks=True,
             output_encoding="utf-8",
         )
-        template = lookup.get_template("/read_unicode_py3k.html")
+        if compat.py3k:
+            template = lookup.get_template("/read_unicode_py3k.html")
+        else:
+            template = lookup.get_template("/read_unicode.html")
         # TODO: I've no idea what encoding this file is, Python 3.1.2
         # won't read the file even with open(...encoding='utf-8') unless
         # errors is specified.   or if there's some quirk in 3.1.2
         # since I'm pretty sure this test worked with py3k when I wrote it.
         template.render(path=self._file_path("internationalization.html"))
 
+    @requires_python_2
+    def test_bytestring_passthru(self):
+        self._do_file_test(
+            "chs_utf8.html",
+            "毛泽东 是 新中国的主席<br/> Welcome 你 to 北京. Welcome 你 to 北京.",
+            default_filters=[],
+            disable_unicode=True,
+            output_encoding=None,
+            template_args={"name": "毛泽东"},
+            filters=flatten_result,
+            unicode_=False,
+        )
+
+        self._do_file_test(
+            "chs_utf8.html",
+            "毛泽东 是 新中国的主席<br/> Welcome 你 to 北京. Welcome 你 to 北京.",
+            disable_unicode=True,
+            output_encoding=None,
+            template_args={"name": "毛泽东"},
+            filters=flatten_result,
+            unicode_=False,
+        )
+
+        template = self._file_template(
+            "chs_utf8.html", output_encoding=None, disable_unicode=True
+        )
+        self.assertRaises(
+            UnicodeDecodeError, template.render_unicode, name="毛泽东"
+        )
+
+        template = Template(
+            "${'Alors vous imaginez ma surprise, au lever"
+            " du jour, quand une drôle de petite voix m’a "
+            "réveillé. Elle disait: « S’il vous plaît… "
+            "dessine-moi un mouton! »'}",
+            output_encoding=None,
+            disable_unicode=True,
+            input_encoding="utf-8",
+        )
+        assert (
+            template.render() == "Alors vous imaginez ma surprise, "
+            "au lever du jour, quand une drôle de petite "
+            "voix m’a réveillé. Elle disait: « S’il vous "
+            "plaît… dessine-moi un mouton! »"
+        )
+        template = Template(
+            "${'Alors vous imaginez ma surprise, au "
+            "lever du jour, quand une drôle de petite "
+            "voix m’a réveillé. Elle disait: « S’il "
+            "vous plaît… dessine-moi un mouton! »'}",
+            input_encoding="utf8",
+            output_encoding="utf8",
+            disable_unicode=False,
+            default_filters=[],
+        )
+        # raises because expression contains an encoded bytestring which cannot
+        # be decoded
+        self.assertRaises(UnicodeDecodeError, template.render)
+
 
 class PageArgsTest(TemplateTest):
     def test_basic(self):
@@ -900,7 +1094,7 @@ class ReservedNameTest(TemplateTest):
                 exceptions.NameConflictError,
                 r"Reserved words passed to render\(\): %s" % name,
                 Template("x").render,
-                **{name: "foo"},
+                **{name: "foo"}
             )
 
     def test_names_in_template(self):
@@ -940,18 +1134,15 @@ class ControlTest(TemplateTest):
     %endfor
 """
         )
-        assert (
-            result_lines(
-                t.render(
-                    y=[
-                        {"test": "one"},
-                        {"foo": "bar"},
-                        {"foo": "bar", "test": "two"},
-                    ]
-                )
+        assert result_lines(
+            t.render(
+                y=[
+                    {"test": "one"},
+                    {"foo": "bar"},
+                    {"foo": "bar", "test": "two"},
+                ]
             )
-            == ["yes x has test", "no x does not have test", "yes x has test"]
-        )
+        ) == ["yes x has test", "no x does not have test", "yes x has test"]
 
     def test_blank_control_1(self):
         self._do_memory_test(
@@ -1167,16 +1358,15 @@ class GlobalsTest(TemplateTest):
 
 class RichTracebackTest(TemplateTest):
     def _do_test_traceback(self, utf8, memory, syntax):
-
         if memory:
             if syntax:
-                source = (
+                source = u(
                     '## coding: utf-8\n<% print "m’a réveillé. '
                     "Elle disait: « S’il vous plaît… dessine-moi "
                     "un mouton! » %>"
                 )
             else:
-                source = (
+                source = u(
                     '## coding: utf-8\n<% print u"m’a réveillé. '
                     "Elle disait: « S’il vous plaît… dessine-moi un "
                     'mouton! »" + str(5/0) %>'
@@ -1226,47 +1416,40 @@ for utf8 in (True, False):
 
 
 class ModuleDirTest(TemplateTest):
-    def teardown_method(self):
+    def tearDown(self):
         import shutil
 
-        shutil.rmtree(config.module_base, True)
+        shutil.rmtree(module_base, True)
 
     def test_basic(self):
         t = self._file_template("modtest.html")
         t2 = self._file_template("subdir/modtest.html")
 
-        eq_(
-            t.module.__file__,
-            os.path.join(config.module_base, "modtest.html.py"),
-        )
+        eq_(t.module.__file__, os.path.join(module_base, "modtest.html.py"))
         eq_(
             t2.module.__file__,
-            os.path.join(config.module_base, "subdir", "modtest.html.py"),
+            os.path.join(module_base, "subdir", "modtest.html.py"),
         )
 
     def test_callable(self):
         def get_modname(filename, uri):
             return os.path.join(
-                config.module_base,
+                module_base,
                 os.path.dirname(uri)[1:],
                 "foo",
                 os.path.basename(filename) + ".py",
             )
 
-        lookup = TemplateLookup(
-            config.template_base, modulename_callable=get_modname
-        )
+        lookup = TemplateLookup(template_base, modulename_callable=get_modname)
         t = lookup.get_template("/modtest.html")
         t2 = lookup.get_template("/subdir/modtest.html")
         eq_(
             t.module.__file__,
-            os.path.join(config.module_base, "foo", "modtest.html.py"),
+            os.path.join(module_base, "foo", "modtest.html.py"),
         )
         eq_(
             t2.module.__file__,
-            os.path.join(
-                config.module_base, "subdir", "foo", "modtest.html.py"
-            ),
+            os.path.join(module_base, "subdir", "foo", "modtest.html.py"),
         )
 
     def test_custom_writer(self):
@@ -1279,17 +1462,17 @@ class ModuleDirTest(TemplateTest):
             f.close()
 
         lookup = TemplateLookup(
-            config.template_base,
+            template_base,
             module_writer=write_module,
-            module_directory=config.module_base,
+            module_directory=module_base,
         )
         lookup.get_template("/modtest.html")
         lookup.get_template("/subdir/modtest.html")
         eq_(
             canary,
             [
-                os.path.join(config.module_base, "modtest.html.py"),
-                os.path.join(config.module_base, "subdir", "modtest.html.py"),
+                os.path.join(module_base, "modtest.html.py"),
+                os.path.join(module_base, "subdir", "modtest.html.py"),
             ],
         )
 
@@ -1443,7 +1626,7 @@ class ModuleTemplateTest(TemplateTest):
         ]
 
 
-class TestTemplateAPI:
+class TestTemplateAPI(unittest.TestCase):
     def test_metadata(self):
         t = Template(
             """
@@ -1633,7 +1816,7 @@ class LexerTest(TemplateTest):
     def _fixture(self):
         from mako.parsetree import TemplateNode, Text
 
-        class MyLexer:
+        class MyLexer(object):
             encoding = "ascii"
 
             def __init__(self, *arg, **kw):
diff --git a/third_party/mako/mako/test/test_tgplugin.py b/third_party/mako/mako/test/test_tgplugin.py
index 38998c2a37a6..df95d00a7409 100644
--- a/third_party/mako/mako/test/test_tgplugin.py
+++ b/third_party/mako/mako/test/test_tgplugin.py
@@ -1,11 +1,10 @@
+from mako import compat
 from mako.ext.turbogears import TGPlugin
-from mako.testing.config import config
-from mako.testing.fixtures import TemplateTest
-from mako.testing.helpers import result_lines
+from test import template_base
+from test import TemplateTest
+from test.util import result_lines
 
-tl = TGPlugin(
-    options=dict(directories=[config.template_base]), extension="html"
-)
+tl = TGPlugin(options=dict(directories=[template_base]), extension="html")
 
 
 class TestTGPlugin(TemplateTest):
@@ -48,6 +47,6 @@ class TestTGPlugin(TemplateTest):
         assert result_lines(tl.render({}, template="/index.html")) == [
             "this is index"
         ]
-        assert result_lines(tl.render({}, template=("/index.html"))) == [
-            "this is index"
-        ]
+        assert result_lines(
+            tl.render({}, template=compat.u("/index.html"))
+        ) == ["this is index"]
diff --git a/third_party/mako/mako/test/test_util.py b/third_party/mako/mako/test/test_util.py
index 95c1cb451960..e40390f7eee5 100644
--- a/third_party/mako/mako/test/test_util.py
+++ b/third_party/mako/mako/test/test_util.py
@@ -1,19 +1,19 @@
+# -*- coding: utf-8 -*-
+
 import os
 import sys
-
-import pytest
+import unittest
 
 from mako import compat
 from mako import exceptions
 from mako import util
-from mako.testing.assertions import assert_raises_message
-from mako.testing.assertions import eq_
-from mako.testing.assertions import in_
-from mako.testing.assertions import ne_
-from mako.testing.assertions import not_in
+from mako.compat import u
+from test import assert_raises_message
+from test import eq_
+from test import skip_if
 
 
-class UtilTest:
+class UtilTest(unittest.TestCase):
     def test_fast_buffer_write(self):
         buf = util.FastEncodingBuffer()
         buf.write("string a ")
@@ -30,7 +30,7 @@ class UtilTest:
         eq_(buf.getvalue(), "string c string d")
 
     def test_fast_buffer_encoded(self):
-        s = "drôl m’a rée « S’il"
+        s = u("drôl m’a rée « S’il")
         buf = util.FastEncodingBuffer(encoding="utf-8")
         buf.write(s[0:10])
         buf.write(s[10:])
@@ -39,18 +39,18 @@ class UtilTest:
     def test_read_file(self):
         fn = os.path.join(os.path.dirname(__file__), "test_util.py")
         data = util.read_file(fn, "rb")
-        assert b"test_util" in data
+        assert "test_util" in str(data)  # str() for py3k
 
-    @pytest.mark.skipif(compat.pypy, reason="Pypy does this differently")
+    @skip_if(lambda: compat.pypy, "Pypy does this differently")
     def test_load_module(self):
-        path = os.path.join(os.path.dirname(__file__), "module_to_import.py")
-        some_module = compat.load_module("test.module_to_import", path)
-
-        not_in("test.module_to_import", sys.modules)
-        in_("some_function", dir(some_module))
-        import test.module_to_import
+        fn = os.path.join(os.path.dirname(__file__), "test_util.py")
+        sys.modules.pop("mako.template")
+        module = compat.load_module("mako.template", fn)
+        self.assertNotIn("mako.template", sys.modules)
+        self.assertIn("UtilTest", dir(module))
+        import mako.template
 
-        ne_(some_module, test.module_to_import)
+        self.assertNotEqual(module, mako.template)
 
     def test_load_plugin_failure(self):
         loader = util.PluginLoader("fakegroup")
diff --git a/third_party/mako/mako/test/testing/dummy.cfg b/third_party/mako/mako/test/testing/dummy.cfg
deleted file mode 100644
index 39644a399b8c..000000000000
--- a/third_party/mako/mako/test/testing/dummy.cfg
+++ /dev/null
@@ -1,25 +0,0 @@
-[boolean_values]
-yes = yes
-one = 1
-true = true
-on = on
-no = no
-zero = 0
-false = false
-off = off
-
-[additional_types]
-decimal_value = 100001.01
-datetime_value = 2021-12-04 00:05:23.283
-
-[type_mismatch]
-int_value = foo
-
-[missing_item]
-present_item = HERE
-
-[basic_values]
-int_value = 15421
-bool_value = true
-float_value = 14.01
-str_value = Ceci n'est pas une chaîne
diff --git a/third_party/mako/mako/test/testing/test_config.py b/third_party/mako/mako/test/testing/test_config.py
deleted file mode 100644
index 680d7a40c96f..000000000000
--- a/third_party/mako/mako/test/testing/test_config.py
+++ /dev/null
@@ -1,176 +0,0 @@
-import configparser
-from dataclasses import dataclass
-from datetime import datetime
-from decimal import Decimal
-from pathlib import Path
-
-import pytest
-
-from mako.testing._config import ConfigValueTypeError
-from mako.testing._config import MissingConfig
-from mako.testing._config import MissingConfigItem
-from mako.testing._config import MissingConfigSection
-from mako.testing._config import ReadsCfg
-from mako.testing.assertions import assert_raises_message_with_given_cause
-from mako.testing.assertions import assert_raises_with_given_cause
-
-PATH_TO_TEST_CONFIG = Path(__file__).parent / "dummy.cfg"
-
-
-@dataclass
-class BasicConfig(ReadsCfg):
-    int_value: int
-    bool_value: bool
-    float_value: float
-    str_value: str
-
-    section_header = "basic_values"
-
-
-@dataclass
-class BooleanConfig(ReadsCfg):
-    yes: bool
-    one: bool
-    true: bool
-    on: bool
-    no: bool
-    zero: bool
-    false: bool
-    off: bool
-
-    section_header = "boolean_values"
-
-
-@dataclass
-class UnsupportedTypesConfig(ReadsCfg):
-    decimal_value: Decimal
-    datetime_value: datetime
-
-    section_header = "additional_types"
-
-
-@dataclass
-class SupportedTypesConfig(ReadsCfg):
-    decimal_value: Decimal
-    datetime_value: datetime
-
-    section_header = "additional_types"
-    converters = {
-        Decimal: lambda v: Decimal(str(v)),
-        datetime: lambda v: datetime.fromisoformat(v),
-    }
-
-
-@dataclass
-class NonexistentSectionConfig(ReadsCfg):
-    some_value: str
-    another_value: str
-
-    section_header = "i_dont_exist"
-
-
-@dataclass
-class TypeMismatchConfig(ReadsCfg):
-    int_value: int
-
-    section_header = "type_mismatch"
-
-
-@dataclass
-class MissingItemConfig(ReadsCfg):
-    present_item: str
-    missing_item: str
-
-    section_header = "missing_item"
-
-
-class BasicConfigTest:
-    @pytest.fixture(scope="class")
-    def config(self):
-        return BasicConfig.from_cfg_file(PATH_TO_TEST_CONFIG)
-
-    def test_coercions(self, config):
-        assert isinstance(config.int_value, int)
-        assert isinstance(config.bool_value, bool)
-        assert isinstance(config.float_value, float)
-        assert isinstance(config.str_value, str)
-
-    def test_values(self, config):
-        assert config.int_value == 15421
-        assert config.bool_value == True
-        assert config.float_value == 14.01
-        assert config.str_value == "Ceci n'est pas une chaîne"
-
-    def test_error_on_loading_from_nonexistent_file(self):
-        assert_raises_with_given_cause(
-            MissingConfig,
-            FileNotFoundError,
-            BasicConfig.from_cfg_file,
-            "./n/o/f/i/l/e/h.ere",
-        )
-
-    def test_error_on_loading_from_nonexistent_section(self):
-        assert_raises_with_given_cause(
-            MissingConfigSection,
-            configparser.NoSectionError,
-            NonexistentSectionConfig.from_cfg_file,
-            PATH_TO_TEST_CONFIG,
-        )
-
-
-class BooleanConfigTest:
-    @pytest.fixture(scope="class")
-    def config(self):
-        return BooleanConfig.from_cfg_file(PATH_TO_TEST_CONFIG)
-
-    def test_values(self, config):
-        assert config.yes is True
-        assert config.one is True
-        assert config.true is True
-        assert config.on is True
-        assert config.no is False
-        assert config.zero is False
-        assert config.false is False
-        assert config.off is False
-
-
-class UnsupportedTypesConfigTest:
-    @pytest.fixture(scope="class")
-    def config(self):
-        return UnsupportedTypesConfig.from_cfg_file(PATH_TO_TEST_CONFIG)
-
-    def test_values(self, config):
-        assert config.decimal_value == "100001.01"
-        assert config.datetime_value == "2021-12-04 00:05:23.283"
-
-
-class SupportedTypesConfigTest:
-    @pytest.fixture(scope="class")
-    def config(self):
-        return SupportedTypesConfig.from_cfg_file(PATH_TO_TEST_CONFIG)
-
-    def test_values(self, config):
-        assert config.decimal_value == Decimal("100001.01")
-        assert config.datetime_value == datetime(2021, 12, 4, 0, 5, 23, 283000)
-
-
-class TypeMismatchConfigTest:
-    def test_error_on_load(self):
-        assert_raises_message_with_given_cause(
-            ConfigValueTypeError,
-            "Wrong value type for int_value",
-            ValueError,
-            TypeMismatchConfig.from_cfg_file,
-            PATH_TO_TEST_CONFIG,
-        )
-
-
-class MissingItemConfigTest:
-    def test_error_on_load(self):
-        assert_raises_message_with_given_cause(
-            MissingConfigItem,
-            "No config item for missing_item",
-            configparser.NoOptionError,
-            MissingItemConfig.from_cfg_file,
-            PATH_TO_TEST_CONFIG,
-        )
diff --git a/third_party/mako/mako/test/util.py b/third_party/mako/mako/test/util.py
new file mode 100644
index 000000000000..29225e232bad
--- /dev/null
+++ b/third_party/mako/mako/test/util.py
@@ -0,0 +1,13 @@
+import re
+
+
+def flatten_result(result):
+    return re.sub(r"[\s\r\n]+", " ", result).strip()
+
+
+def result_lines(result):
+    return [
+        x.strip()
+        for x in re.split(r"\r?\n", re.sub(r" +", " ", result))
+        if x.strip() != ""
+    ]
diff --git a/third_party/mako/mako/tox.ini b/third_party/mako/mako/tox.ini
index d8edd3cb03da..d3ef981b012f 100644
--- a/third_party/mako/mako/tox.ini
+++ b/third_party/mako/mako/tox.ini
@@ -5,6 +5,7 @@ envlist = py
 cov_args=--cov=mako --cov-report term --cov-report xml
 
 deps=pytest>=3.1.0
+     mock
      beaker
      markupsafe
      pygments
@@ -16,7 +17,7 @@ deps=pytest>=3.1.0
 setenv=
     cov: COVERAGE={[testenv]cov_args}
 
-commands=pytest {env:COVERAGE:} {posargs}
+commands=py.test {env:COVERAGE:} {posargs}
 
 
 [testenv:pep8]
@@ -30,7 +31,4 @@ deps=
       pydocstyle<4.0.0
       # used by flake8-rst-docstrings
       pygments
-      black==21.9b0
-commands =
-    flake8 ./mako/ ./test/ setup.py --exclude test/templates,test/foo  {posargs}
-    black --check .
+commands = flake8 ./mako/ ./test/ setup.py --exclude test/templates,test/foo  {posargs}
-- 
2.34.1

