summaryrefslogtreecommitdiffstats
path: root/third_party/python/MarkupSafe
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/python/MarkupSafe/CHANGES.rst112
-rw-r--r--third_party/python/MarkupSafe/LICENSE.rst28
-rw-r--r--third_party/python/MarkupSafe/MANIFEST.in9
-rw-r--r--third_party/python/MarkupSafe/PKG-INFO98
-rw-r--r--third_party/python/MarkupSafe/README.rst69
-rw-r--r--third_party/python/MarkupSafe/requirements/dev.txt132
-rw-r--r--third_party/python/MarkupSafe/requirements/docs.txt67
-rw-r--r--third_party/python/MarkupSafe/requirements/tests.txt22
-rw-r--r--third_party/python/MarkupSafe/requirements/typing.txt14
-rw-r--r--third_party/python/MarkupSafe/setup.cfg86
-rw-r--r--third_party/python/MarkupSafe/setup.py81
-rw-r--r--third_party/python/MarkupSafe/src/MarkupSafe.egg-info/PKG-INFO98
-rw-r--r--third_party/python/MarkupSafe/src/MarkupSafe.egg-info/SOURCES.txt34
-rw-r--r--third_party/python/MarkupSafe/src/MarkupSafe.egg-info/dependency_links.txt1
-rw-r--r--third_party/python/MarkupSafe/src/MarkupSafe.egg-info/top_level.txt1
-rw-r--r--third_party/python/MarkupSafe/src/markupsafe/__init__.py288
-rw-r--r--third_party/python/MarkupSafe/src/markupsafe/_native.py75
-rw-r--r--third_party/python/MarkupSafe/src/markupsafe/_speedups.c339
-rw-r--r--third_party/python/MarkupSafe/src/markupsafe/_speedups.pyi9
-rw-r--r--third_party/python/MarkupSafe/src/markupsafe/py.typed0
-rw-r--r--third_party/python/MarkupSafe/tox.ini24
21 files changed, 1587 insertions, 0 deletions
diff --git a/third_party/python/MarkupSafe/CHANGES.rst b/third_party/python/MarkupSafe/CHANGES.rst
new file mode 100644
index 0000000000..e50fc98021
--- /dev/null
+++ b/third_party/python/MarkupSafe/CHANGES.rst
@@ -0,0 +1,112 @@
+Version 2.0.1
+-------------
+
+Released 2021-05-18
+
+- Mark top-level names as exported so type checking understands
+ imports in user projects. :pr:`215`
+- Fix some types that weren't available in Python 3.6.0. :pr:`215`
+
+
+Version 2.0.0
+-------------
+
+Released 2021-05-11
+
+- Drop Python 2.7, 3.4, and 3.5 support.
+- ``Markup.unescape`` uses :func:`html.unescape` to support HTML5
+ character references. :pr:`117`
+- Add type annotations for static typing tools. :pr:`149`
+
+
+Version 1.1.1
+-------------
+
+Released 2019-02-23
+
+- Fix segfault when ``__html__`` method raises an exception when using
+ the C speedups. The exception is now propagated correctly. :pr:`109`
+
+
+Version 1.1.0
+-------------
+
+Released 2018-11-05
+
+- Drop support for Python 2.6 and 3.3.
+- Build wheels for Linux, Mac, and Windows, allowing systems without
+ a compiler to take advantage of the C extension speedups. :pr:`104`
+- Use newer CPython API on Python 3, resulting in a 1.5x speedup.
+ :pr`64`
+- ``escape`` wraps ``__html__`` result in ``Markup``, consistent with
+ documented behavior. :pr:`69`
+
+
+Version 1.0
+-----------
+
+Released 2017-03-07
+
+- Fixed custom types not invoking ``__unicode__`` when used with
+ ``format()``.
+- Added ``__version__`` module attribute.
+- Improve unescape code to leave lone ampersands alone.
+
+
+Version 0.18
+------------
+
+Released 2013-05-22
+
+- Fixed ``__mul__`` and string splitting on Python 3.
+
+
+Version 0.17
+------------
+
+Released 2013-05-21
+
+- Fixed a bug with broken interpolation on tuples.
+
+
+Version 0.16
+------------
+
+Released 2013-05-20
+
+- Improved Python 3 Support and removed 2to3.
+- Removed support for Python 3.2 and 2.5.
+
+
+Version 0.15
+------------
+
+Released 2011-07-20
+
+- Fixed a typo that caused the library to fail to install on pypy and
+ jython.
+
+
+Version 0.14
+------------
+
+Released 2011-07-20
+
+- Release fix for 0.13.
+
+
+Version 0.13
+------------
+
+Released 2011-07-20
+
+- Do not attempt to compile extension for PyPy or Jython.
+- Work around some 64bit Windows issues.
+
+
+Version 0.12
+------------
+
+Released 2011-02-17
+
+- Improved PyPy compatibility.
diff --git a/third_party/python/MarkupSafe/LICENSE.rst b/third_party/python/MarkupSafe/LICENSE.rst
new file mode 100644
index 0000000000..9d227a0cc4
--- /dev/null
+++ b/third_party/python/MarkupSafe/LICENSE.rst
@@ -0,0 +1,28 @@
+Copyright 2010 Pallets
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/third_party/python/MarkupSafe/MANIFEST.in b/third_party/python/MarkupSafe/MANIFEST.in
new file mode 100644
index 0000000000..7dfa3f60c7
--- /dev/null
+++ b/third_party/python/MarkupSafe/MANIFEST.in
@@ -0,0 +1,9 @@
+include CHANGES.rst
+include tox.ini
+include requirements/*.txt
+graft docs
+prune docs/_build
+graft tests
+include src/markupsafe/py.typed
+include src/markupsafe/*.pyi
+global-exclude *.pyc
diff --git a/third_party/python/MarkupSafe/PKG-INFO b/third_party/python/MarkupSafe/PKG-INFO
new file mode 100644
index 0000000000..bd2f99cba8
--- /dev/null
+++ b/third_party/python/MarkupSafe/PKG-INFO
@@ -0,0 +1,98 @@
+Metadata-Version: 2.1
+Name: MarkupSafe
+Version: 2.0.1
+Summary: Safely add untrusted strings to HTML/XML markup.
+Home-page: https://palletsprojects.com/p/markupsafe/
+Author: Armin Ronacher
+Author-email: armin.ronacher@active-4.com
+Maintainer: Pallets
+Maintainer-email: contact@palletsprojects.com
+License: BSD-3-Clause
+Project-URL: Donate, https://palletsprojects.com/donate
+Project-URL: Documentation, https://markupsafe.palletsprojects.com/
+Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/
+Project-URL: Source Code, https://github.com/pallets/markupsafe/
+Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/
+Project-URL: Twitter, https://twitter.com/PalletsTeam
+Project-URL: Chat, https://discord.gg/pallets
+Description: MarkupSafe
+ ==========
+
+ MarkupSafe implements a text object that escapes characters so it is
+ safe to use in HTML and XML. Characters that have special meanings are
+ replaced so that they display as the actual characters. This mitigates
+ injection attacks, meaning untrusted user input can safely be displayed
+ on a page.
+
+
+ Installing
+ ----------
+
+ Install and update using `pip`_:
+
+ .. code-block:: text
+
+ pip install -U MarkupSafe
+
+ .. _pip: https://pip.pypa.io/en/stable/quickstart/
+
+
+ Examples
+ --------
+
+ .. code-block:: pycon
+
+ >>> from markupsafe import Markup, escape
+
+ >>> # escape replaces special characters and wraps in Markup
+ >>> escape("<script>alert(document.cookie);</script>")
+ Markup('&lt;script&gt;alert(document.cookie);&lt;/script&gt;')
+
+ >>> # wrap in Markup to mark text "safe" and prevent escaping
+ >>> Markup("<strong>Hello</strong>")
+ Markup('<strong>hello</strong>')
+
+ >>> escape(Markup("<strong>Hello</strong>"))
+ Markup('<strong>hello</strong>')
+
+ >>> # Markup is a str subclass
+ >>> # methods and operators escape their arguments
+ >>> template = Markup("Hello <em>{name}</em>")
+ >>> template.format(name='"World"')
+ Markup('Hello <em>&#34;World&#34;</em>')
+
+
+ Donate
+ ------
+
+ The Pallets organization develops and supports MarkupSafe and other
+ popular packages. In order to grow the community of contributors and
+ users, and allow the maintainers to devote more time to the projects,
+ `please donate today`_.
+
+ .. _please donate today: https://palletsprojects.com/donate
+
+
+ Links
+ -----
+
+ - Documentation: https://markupsafe.palletsprojects.com/
+ - Changes: https://markupsafe.palletsprojects.com/changes/
+ - PyPI Releases: https://pypi.org/project/MarkupSafe/
+ - Source Code: https://github.com/pallets/markupsafe/
+ - Issue Tracker: https://github.com/pallets/markupsafe/issues/
+ - Website: https://palletsprojects.com/p/markupsafe/
+ - Twitter: https://twitter.com/PalletsTeam
+ - Chat: https://discord.gg/pallets
+
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Classifier: Topic :: Text Processing :: Markup :: HTML
+Requires-Python: >=3.6
+Description-Content-Type: text/x-rst
diff --git a/third_party/python/MarkupSafe/README.rst b/third_party/python/MarkupSafe/README.rst
new file mode 100644
index 0000000000..273e1db40f
--- /dev/null
+++ b/third_party/python/MarkupSafe/README.rst
@@ -0,0 +1,69 @@
+MarkupSafe
+==========
+
+MarkupSafe implements a text object that escapes characters so it is
+safe to use in HTML and XML. Characters that have special meanings are
+replaced so that they display as the actual characters. This mitigates
+injection attacks, meaning untrusted user input can safely be displayed
+on a page.
+
+
+Installing
+----------
+
+Install and update using `pip`_:
+
+.. code-block:: text
+
+ pip install -U MarkupSafe
+
+.. _pip: https://pip.pypa.io/en/stable/quickstart/
+
+
+Examples
+--------
+
+.. code-block:: pycon
+
+ >>> from markupsafe import Markup, escape
+
+ >>> # escape replaces special characters and wraps in Markup
+ >>> escape("<script>alert(document.cookie);</script>")
+ Markup('&lt;script&gt;alert(document.cookie);&lt;/script&gt;')
+
+ >>> # wrap in Markup to mark text "safe" and prevent escaping
+ >>> Markup("<strong>Hello</strong>")
+ Markup('<strong>hello</strong>')
+
+ >>> escape(Markup("<strong>Hello</strong>"))
+ Markup('<strong>hello</strong>')
+
+ >>> # Markup is a str subclass
+ >>> # methods and operators escape their arguments
+ >>> template = Markup("Hello <em>{name}</em>")
+ >>> template.format(name='"World"')
+ Markup('Hello <em>&#34;World&#34;</em>')
+
+
+Donate
+------
+
+The Pallets organization develops and supports MarkupSafe and other
+popular packages. In order to grow the community of contributors and
+users, and allow the maintainers to devote more time to the projects,
+`please donate today`_.
+
+.. _please donate today: https://palletsprojects.com/donate
+
+
+Links
+-----
+
+- Documentation: https://markupsafe.palletsprojects.com/
+- Changes: https://markupsafe.palletsprojects.com/changes/
+- PyPI Releases: https://pypi.org/project/MarkupSafe/
+- Source Code: https://github.com/pallets/markupsafe/
+- Issue Tracker: https://github.com/pallets/markupsafe/issues/
+- Website: https://palletsprojects.com/p/markupsafe/
+- Twitter: https://twitter.com/PalletsTeam
+- Chat: https://discord.gg/pallets
diff --git a/third_party/python/MarkupSafe/requirements/dev.txt b/third_party/python/MarkupSafe/requirements/dev.txt
new file mode 100644
index 0000000000..0dbc2bc860
--- /dev/null
+++ b/third_party/python/MarkupSafe/requirements/dev.txt
@@ -0,0 +1,132 @@
+#
+# This file is autogenerated by pip-compile
+# To update, run:
+#
+# pip-compile requirements/dev.in
+#
+alabaster==0.7.12
+ # via sphinx
+appdirs==1.4.4
+ # via virtualenv
+attrs==21.2.0
+ # via pytest
+babel==2.9.1
+ # via sphinx
+certifi==2020.12.5
+ # via requests
+cfgv==3.2.0
+ # via pre-commit
+chardet==4.0.0
+ # via requests
+click==8.0.0
+ # via pip-tools
+distlib==0.3.1
+ # via virtualenv
+docutils==0.17.1
+ # via sphinx
+filelock==3.0.12
+ # via
+ # tox
+ # virtualenv
+identify==2.2.4
+ # via pre-commit
+idna==2.10
+ # via requests
+imagesize==1.2.0
+ # via sphinx
+iniconfig==1.1.1
+ # via pytest
+jinja2==3.0.0
+ # via sphinx
+markupsafe==2.0.0
+ # via jinja2
+mypy-extensions==0.4.3
+ # via mypy
+mypy==0.812
+ # via -r requirements/typing.in
+nodeenv==1.6.0
+ # via pre-commit
+packaging==20.9
+ # via
+ # pallets-sphinx-themes
+ # pytest
+ # sphinx
+ # tox
+pallets-sphinx-themes==2.0.0
+ # via -r requirements/docs.in
+pep517==0.10.0
+ # via pip-tools
+pip-tools==6.1.0
+ # via -r requirements/dev.in
+pluggy==0.13.1
+ # via
+ # pytest
+ # tox
+pre-commit==2.12.1
+ # via -r requirements/dev.in
+py==1.10.0
+ # via
+ # pytest
+ # tox
+pygments==2.9.0
+ # via sphinx
+pyparsing==2.4.7
+ # via packaging
+pytest==6.2.4
+ # via -r requirements/tests.in
+pytz==2021.1
+ # via babel
+pyyaml==5.4.1
+ # via pre-commit
+requests==2.25.1
+ # via sphinx
+six==1.16.0
+ # via
+ # tox
+ # virtualenv
+snowballstemmer==2.1.0
+ # via sphinx
+sphinx-issues==1.2.0
+ # via -r requirements/docs.in
+git+https://github.com/sphinx-doc/sphinx.git@96dbe5e3
+ # via
+ # -r requirements/docs.in
+ # pallets-sphinx-themes
+ # sphinx-issues
+ # sphinxcontrib-log-cabinet
+sphinxcontrib-applehelp==1.0.2
+ # via sphinx
+sphinxcontrib-devhelp==1.0.2
+ # via sphinx
+sphinxcontrib-htmlhelp==1.0.3
+ # via sphinx
+sphinxcontrib-jsmath==1.0.1
+ # via sphinx
+sphinxcontrib-log-cabinet==1.0.1
+ # via -r requirements/docs.in
+sphinxcontrib-qthelp==1.0.3
+ # via sphinx
+sphinxcontrib-serializinghtml==1.1.4
+ # via sphinx
+toml==0.10.2
+ # via
+ # pep517
+ # pre-commit
+ # pytest
+ # tox
+tox==3.23.1
+ # via -r requirements/dev.in
+typed-ast==1.4.3
+ # via mypy
+typing-extensions==3.10.0.0
+ # via mypy
+urllib3==1.26.4
+ # via requests
+virtualenv==20.4.6
+ # via
+ # pre-commit
+ # tox
+
+# The following packages are considered to be unsafe in a requirements file:
+# pip
+# setuptools
diff --git a/third_party/python/MarkupSafe/requirements/docs.txt b/third_party/python/MarkupSafe/requirements/docs.txt
new file mode 100644
index 0000000000..ec9cfcb668
--- /dev/null
+++ b/third_party/python/MarkupSafe/requirements/docs.txt
@@ -0,0 +1,67 @@
+#
+# This file is autogenerated by pip-compile
+# To update, run:
+#
+# pip-compile requirements/docs.in
+#
+alabaster==0.7.12
+ # via sphinx
+babel==2.9.1
+ # via sphinx
+certifi==2020.12.5
+ # via requests
+chardet==4.0.0
+ # via requests
+docutils==0.17.1
+ # via sphinx
+idna==2.10
+ # via requests
+imagesize==1.2.0
+ # via sphinx
+jinja2==3.0.0
+ # via sphinx
+markupsafe==2.0.0
+ # via jinja2
+packaging==20.9
+ # via
+ # pallets-sphinx-themes
+ # sphinx
+pallets-sphinx-themes==2.0.0
+ # via -r requirements/docs.in
+pygments==2.9.0
+ # via sphinx
+pyparsing==2.4.7
+ # via packaging
+pytz==2021.1
+ # via babel
+requests==2.25.1
+ # via sphinx
+snowballstemmer==2.1.0
+ # via sphinx
+sphinx-issues==1.2.0
+ # via -r requirements/docs.in
+git+https://github.com/sphinx-doc/sphinx.git@96dbe5e3
+ # via
+ # -r requirements/docs.in
+ # pallets-sphinx-themes
+ # sphinx-issues
+ # sphinxcontrib-log-cabinet
+sphinxcontrib-applehelp==1.0.2
+ # via sphinx
+sphinxcontrib-devhelp==1.0.2
+ # via sphinx
+sphinxcontrib-htmlhelp==1.0.3
+ # via sphinx
+sphinxcontrib-jsmath==1.0.1
+ # via sphinx
+sphinxcontrib-log-cabinet==1.0.1
+ # via -r requirements/docs.in
+sphinxcontrib-qthelp==1.0.3
+ # via sphinx
+sphinxcontrib-serializinghtml==1.1.4
+ # via sphinx
+urllib3==1.26.4
+ # via requests
+
+# The following packages are considered to be unsafe in a requirements file:
+# setuptools
diff --git a/third_party/python/MarkupSafe/requirements/tests.txt b/third_party/python/MarkupSafe/requirements/tests.txt
new file mode 100644
index 0000000000..4ff31e3187
--- /dev/null
+++ b/third_party/python/MarkupSafe/requirements/tests.txt
@@ -0,0 +1,22 @@
+#
+# This file is autogenerated by pip-compile
+# To update, run:
+#
+# pip-compile requirements/tests.in
+#
+attrs==21.2.0
+ # via pytest
+iniconfig==1.1.1
+ # via pytest
+packaging==20.9
+ # via pytest
+pluggy==0.13.1
+ # via pytest
+py==1.10.0
+ # via pytest
+pyparsing==2.4.7
+ # via packaging
+pytest==6.2.4
+ # via -r requirements/tests.in
+toml==0.10.2
+ # via pytest
diff --git a/third_party/python/MarkupSafe/requirements/typing.txt b/third_party/python/MarkupSafe/requirements/typing.txt
new file mode 100644
index 0000000000..0e342aaad8
--- /dev/null
+++ b/third_party/python/MarkupSafe/requirements/typing.txt
@@ -0,0 +1,14 @@
+#
+# This file is autogenerated by pip-compile
+# To update, run:
+#
+# pip-compile requirements/typing.in
+#
+mypy-extensions==0.4.3
+ # via mypy
+mypy==0.812
+ # via -r requirements/typing.in
+typed-ast==1.4.3
+ # via mypy
+typing-extensions==3.10.0.0
+ # via mypy
diff --git a/third_party/python/MarkupSafe/setup.cfg b/third_party/python/MarkupSafe/setup.cfg
new file mode 100644
index 0000000000..1bbc1d4ceb
--- /dev/null
+++ b/third_party/python/MarkupSafe/setup.cfg
@@ -0,0 +1,86 @@
+[metadata]
+name = MarkupSafe
+version = attr: markupsafe.__version__
+url = https://palletsprojects.com/p/markupsafe/
+project_urls =
+ Donate = https://palletsprojects.com/donate
+ Documentation = https://markupsafe.palletsprojects.com/
+ Changes = https://markupsafe.palletsprojects.com/changes/
+ Source Code = https://github.com/pallets/markupsafe/
+ Issue Tracker = https://github.com/pallets/markupsafe/issues/
+ Twitter = https://twitter.com/PalletsTeam
+ Chat = https://discord.gg/pallets
+license = BSD-3-Clause
+license_files = LICENSE.rst
+author = Armin Ronacher
+author_email = armin.ronacher@active-4.com
+maintainer = Pallets
+maintainer_email = contact@palletsprojects.com
+description = Safely add untrusted strings to HTML/XML markup.
+long_description = file: README.rst
+long_description_content_type = text/x-rst
+classifiers =
+ Development Status :: 5 - Production/Stable
+ Environment :: Web Environment
+ Intended Audience :: Developers
+ License :: OSI Approved :: BSD License
+ Operating System :: OS Independent
+ Programming Language :: Python
+ Topic :: Internet :: WWW/HTTP :: Dynamic Content
+ Topic :: Text Processing :: Markup :: HTML
+
+[options]
+packages = find:
+package_dir = = src
+include_package_data = true
+python_requires = >= 3.6
+
+[options.packages.find]
+where = src
+
+[tool:pytest]
+testpaths = tests
+filterwarnings =
+ error
+
+[coverage:run]
+branch = true
+source =
+ markupsafe
+ tests
+
+[coverage:paths]
+source =
+ src
+ */site-packages
+
+[flake8]
+select = B, E, F, W, B9
+ignore =
+ E203
+ E501
+ E722
+ W503
+max-line-length = 80
+
+[mypy]
+files = src/markupsafe
+python_version = 3.6
+disallow_subclassing_any = True
+disallow_untyped_calls = True
+disallow_untyped_defs = True
+disallow_incomplete_defs = True
+no_implicit_optional = True
+local_partial_types = True
+no_implicit_reexport = True
+strict_equality = True
+warn_redundant_casts = True
+warn_unused_configs = True
+warn_unused_ignores = True
+warn_return_any = True
+warn_unreachable = True
+
+[egg_info]
+tag_build =
+tag_date = 0
+
diff --git a/third_party/python/MarkupSafe/setup.py b/third_party/python/MarkupSafe/setup.py
new file mode 100644
index 0000000000..c6ee5bf176
--- /dev/null
+++ b/third_party/python/MarkupSafe/setup.py
@@ -0,0 +1,81 @@
+import os
+import platform
+import sys
+from distutils.errors import CCompilerError
+from distutils.errors import DistutilsExecError
+from distutils.errors import DistutilsPlatformError
+
+from setuptools import Extension
+from setuptools import setup
+from setuptools.command.build_ext import build_ext
+
+ext_modules = [Extension("markupsafe._speedups", ["src/markupsafe/_speedups.c"])]
+
+
+class BuildFailed(Exception):
+ pass
+
+
+class ve_build_ext(build_ext):
+ """This class allows C extension building to fail."""
+
+ def run(self):
+ try:
+ build_ext.run(self)
+ except DistutilsPlatformError:
+ raise BuildFailed()
+
+ def build_extension(self, ext):
+ try:
+ build_ext.build_extension(self, ext)
+ except (CCompilerError, DistutilsExecError, DistutilsPlatformError):
+ raise BuildFailed()
+ except ValueError:
+ # this can happen on Windows 64 bit, see Python issue 7511
+ if "'path'" in str(sys.exc_info()[1]): # works with Python 2 and 3
+ raise BuildFailed()
+ raise
+
+
+def run_setup(with_binary):
+ setup(
+ name="MarkupSafe",
+ cmdclass={"build_ext": ve_build_ext},
+ ext_modules=ext_modules if with_binary else [],
+ )
+
+
+def show_message(*lines):
+ print("=" * 74)
+ for line in lines:
+ print(line)
+ print("=" * 74)
+
+
+supports_speedups = platform.python_implementation() not in {"PyPy", "Jython"}
+
+if os.environ.get("CIBUILDWHEEL", "0") == "1" and supports_speedups:
+ run_setup(True)
+elif supports_speedups:
+ try:
+ run_setup(True)
+ except BuildFailed:
+ show_message(
+ "WARNING: The C extension could not be compiled, speedups"
+ " are not enabled.",
+ "Failure information, if any, is above.",
+ "Retrying the build without the C extension now.",
+ )
+ run_setup(False)
+ show_message(
+ "WARNING: The C extension could not be compiled, speedups"
+ " are not enabled.",
+ "Plain-Python build succeeded.",
+ )
+else:
+ run_setup(False)
+ show_message(
+ "WARNING: C extensions are not supported on this Python"
+ " platform, speedups are not enabled.",
+ "Plain-Python build succeeded.",
+ )
diff --git a/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/PKG-INFO b/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/PKG-INFO
new file mode 100644
index 0000000000..bd2f99cba8
--- /dev/null
+++ b/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/PKG-INFO
@@ -0,0 +1,98 @@
+Metadata-Version: 2.1
+Name: MarkupSafe
+Version: 2.0.1
+Summary: Safely add untrusted strings to HTML/XML markup.
+Home-page: https://palletsprojects.com/p/markupsafe/
+Author: Armin Ronacher
+Author-email: armin.ronacher@active-4.com
+Maintainer: Pallets
+Maintainer-email: contact@palletsprojects.com
+License: BSD-3-Clause
+Project-URL: Donate, https://palletsprojects.com/donate
+Project-URL: Documentation, https://markupsafe.palletsprojects.com/
+Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/
+Project-URL: Source Code, https://github.com/pallets/markupsafe/
+Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/
+Project-URL: Twitter, https://twitter.com/PalletsTeam
+Project-URL: Chat, https://discord.gg/pallets
+Description: MarkupSafe
+ ==========
+
+ MarkupSafe implements a text object that escapes characters so it is
+ safe to use in HTML and XML. Characters that have special meanings are
+ replaced so that they display as the actual characters. This mitigates
+ injection attacks, meaning untrusted user input can safely be displayed
+ on a page.
+
+
+ Installing
+ ----------
+
+ Install and update using `pip`_:
+
+ .. code-block:: text
+
+ pip install -U MarkupSafe
+
+ .. _pip: https://pip.pypa.io/en/stable/quickstart/
+
+
+ Examples
+ --------
+
+ .. code-block:: pycon
+
+ >>> from markupsafe import Markup, escape
+
+ >>> # escape replaces special characters and wraps in Markup
+ >>> escape("<script>alert(document.cookie);</script>")
+ Markup('&lt;script&gt;alert(document.cookie);&lt;/script&gt;')
+
+ >>> # wrap in Markup to mark text "safe" and prevent escaping
+ >>> Markup("<strong>Hello</strong>")
+ Markup('<strong>hello</strong>')
+
+ >>> escape(Markup("<strong>Hello</strong>"))
+ Markup('<strong>hello</strong>')
+
+ >>> # Markup is a str subclass
+ >>> # methods and operators escape their arguments
+ >>> template = Markup("Hello <em>{name}</em>")
+ >>> template.format(name='"World"')
+ Markup('Hello <em>&#34;World&#34;</em>')
+
+
+ Donate
+ ------
+
+ The Pallets organization develops and supports MarkupSafe and other
+ popular packages. In order to grow the community of contributors and
+ users, and allow the maintainers to devote more time to the projects,
+ `please donate today`_.
+
+ .. _please donate today: https://palletsprojects.com/donate
+
+
+ Links
+ -----
+
+ - Documentation: https://markupsafe.palletsprojects.com/
+ - Changes: https://markupsafe.palletsprojects.com/changes/
+ - PyPI Releases: https://pypi.org/project/MarkupSafe/
+ - Source Code: https://github.com/pallets/markupsafe/
+ - Issue Tracker: https://github.com/pallets/markupsafe/issues/
+ - Website: https://palletsprojects.com/p/markupsafe/
+ - Twitter: https://twitter.com/PalletsTeam
+ - Chat: https://discord.gg/pallets
+
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Classifier: Topic :: Text Processing :: Markup :: HTML
+Requires-Python: >=3.6
+Description-Content-Type: text/x-rst
diff --git a/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/SOURCES.txt b/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/SOURCES.txt
new file mode 100644
index 0000000000..3f8c1a146d
--- /dev/null
+++ b/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/SOURCES.txt
@@ -0,0 +1,34 @@
+CHANGES.rst
+LICENSE.rst
+MANIFEST.in
+README.rst
+setup.cfg
+setup.py
+tox.ini
+docs/Makefile
+docs/changes.rst
+docs/conf.py
+docs/escaping.rst
+docs/formatting.rst
+docs/html.rst
+docs/index.rst
+docs/license.rst
+docs/make.bat
+requirements/dev.txt
+requirements/docs.txt
+requirements/tests.txt
+requirements/typing.txt
+src/MarkupSafe.egg-info/PKG-INFO
+src/MarkupSafe.egg-info/SOURCES.txt
+src/MarkupSafe.egg-info/dependency_links.txt
+src/MarkupSafe.egg-info/top_level.txt
+src/markupsafe/__init__.py
+src/markupsafe/_native.py
+src/markupsafe/_speedups.c
+src/markupsafe/_speedups.pyi
+src/markupsafe/py.typed
+tests/conftest.py
+tests/test_escape.py
+tests/test_exception_custom_html.py
+tests/test_leak.py
+tests/test_markupsafe.py \ No newline at end of file
diff --git a/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/dependency_links.txt b/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/dependency_links.txt
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/top_level.txt b/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/top_level.txt
new file mode 100644
index 0000000000..75bf729258
--- /dev/null
+++ b/third_party/python/MarkupSafe/src/MarkupSafe.egg-info/top_level.txt
@@ -0,0 +1 @@
+markupsafe
diff --git a/third_party/python/MarkupSafe/src/markupsafe/__init__.py b/third_party/python/MarkupSafe/src/markupsafe/__init__.py
new file mode 100644
index 0000000000..d331ac3622
--- /dev/null
+++ b/third_party/python/MarkupSafe/src/markupsafe/__init__.py
@@ -0,0 +1,288 @@
+import functools
+import re
+import string
+import typing as t
+
+if t.TYPE_CHECKING:
+ import typing_extensions as te
+
+ class HasHTML(te.Protocol):
+ def __html__(self) -> str:
+ pass
+
+
+__version__ = "2.0.1"
+
+_striptags_re = re.compile(r"(<!--.*?-->|<[^>]*>)")
+
+
+def _simple_escaping_wrapper(name: str) -> t.Callable[..., "Markup"]:
+ orig = getattr(str, name)
+
+ @functools.wraps(orig)
+ def wrapped(self: "Markup", *args: t.Any, **kwargs: t.Any) -> "Markup":
+ args = _escape_argspec(list(args), enumerate(args), self.escape) # type: ignore
+ _escape_argspec(kwargs, kwargs.items(), self.escape)
+ return self.__class__(orig(self, *args, **kwargs))
+
+ return wrapped
+
+
+class Markup(str):
+ """A string that is ready to be safely inserted into an HTML or XML
+ document, either because it was escaped or because it was marked
+ safe.
+
+ Passing an object to the constructor converts it to text and wraps
+ it to mark it safe without escaping. To escape the text, use the
+ :meth:`escape` class method instead.
+
+ >>> Markup("Hello, <em>World</em>!")
+ Markup('Hello, <em>World</em>!')
+ >>> Markup(42)
+ Markup('42')
+ >>> Markup.escape("Hello, <em>World</em>!")
+ Markup('Hello &lt;em&gt;World&lt;/em&gt;!')
+
+ This implements the ``__html__()`` interface that some frameworks
+ use. Passing an object that implements ``__html__()`` will wrap the
+ output of that method, marking it safe.
+
+ >>> class Foo:
+ ... def __html__(self):
+ ... return '<a href="/foo">foo</a>'
+ ...
+ >>> Markup(Foo())
+ Markup('<a href="/foo">foo</a>')
+
+ This is a subclass of :class:`str`. It has the same methods, but
+ escapes their arguments and returns a ``Markup`` instance.
+
+ >>> Markup("<em>%s</em>") % ("foo & bar",)
+ Markup('<em>foo &amp; bar</em>')
+ >>> Markup("<em>Hello</em> ") + "<foo>"
+ Markup('<em>Hello</em> &lt;foo&gt;')
+ """
+
+ __slots__ = ()
+
+ def __new__(
+ cls, base: t.Any = "", encoding: t.Optional[str] = None, errors: str = "strict"
+ ) -> "Markup":
+ if hasattr(base, "__html__"):
+ base = base.__html__()
+
+ if encoding is None:
+ return super().__new__(cls, base)
+
+ return super().__new__(cls, base, encoding, errors)
+
+ def __html__(self) -> "Markup":
+ return self
+
+ def __add__(self, other: t.Union[str, "HasHTML"]) -> "Markup":
+ if isinstance(other, str) or hasattr(other, "__html__"):
+ return self.__class__(super().__add__(self.escape(other)))
+
+ return NotImplemented
+
+ def __radd__(self, other: t.Union[str, "HasHTML"]) -> "Markup":
+ if isinstance(other, str) or hasattr(other, "__html__"):
+ return self.escape(other).__add__(self)
+
+ return NotImplemented
+
+ def __mul__(self, num: int) -> "Markup":
+ if isinstance(num, int):
+ return self.__class__(super().__mul__(num))
+
+ return NotImplemented # type: ignore
+
+ __rmul__ = __mul__
+
+ def __mod__(self, arg: t.Any) -> "Markup":
+ if isinstance(arg, tuple):
+ arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg)
+ else:
+ arg = _MarkupEscapeHelper(arg, self.escape)
+
+ return self.__class__(super().__mod__(arg))
+
+ def __repr__(self) -> str:
+ return f"{self.__class__.__name__}({super().__repr__()})"
+
+ def join(self, seq: t.Iterable[t.Union[str, "HasHTML"]]) -> "Markup":
+ return self.__class__(super().join(map(self.escape, seq)))
+
+ join.__doc__ = str.join.__doc__
+
+ def split( # type: ignore
+ self, sep: t.Optional[str] = None, maxsplit: int = -1
+ ) -> t.List["Markup"]:
+ return [self.__class__(v) for v in super().split(sep, maxsplit)]
+
+ split.__doc__ = str.split.__doc__
+
+ def rsplit( # type: ignore
+ self, sep: t.Optional[str] = None, maxsplit: int = -1
+ ) -> t.List["Markup"]:
+ return [self.__class__(v) for v in super().rsplit(sep, maxsplit)]
+
+ rsplit.__doc__ = str.rsplit.__doc__
+
+ def splitlines(self, keepends: bool = False) -> t.List["Markup"]: # type: ignore
+ return [self.__class__(v) for v in super().splitlines(keepends)]
+
+ splitlines.__doc__ = str.splitlines.__doc__
+
+ def unescape(self) -> str:
+ """Convert escaped markup back into a text string. This replaces
+ HTML entities with the characters they represent.
+
+ >>> Markup("Main &raquo; <em>About</em>").unescape()
+ 'Main » <em>About</em>'
+ """
+ from html import unescape
+
+ return unescape(str(self))
+
+ def striptags(self) -> str:
+ """:meth:`unescape` the markup, remove tags, and normalize
+ whitespace to single spaces.
+
+ >>> Markup("Main &raquo;\t<em>About</em>").striptags()
+ 'Main » About'
+ """
+ stripped = " ".join(_striptags_re.sub("", self).split())
+ return Markup(stripped).unescape()
+
+ @classmethod
+ def escape(cls, s: t.Any) -> "Markup":
+ """Escape a string. Calls :func:`escape` and ensures that for
+ subclasses the correct type is returned.
+ """
+ rv = escape(s)
+
+ if rv.__class__ is not cls:
+ return cls(rv)
+
+ return rv
+
+ for method in (
+ "__getitem__",
+ "capitalize",
+ "title",
+ "lower",
+ "upper",
+ "replace",
+ "ljust",
+ "rjust",
+ "lstrip",
+ "rstrip",
+ "center",
+ "strip",
+ "translate",
+ "expandtabs",
+ "swapcase",
+ "zfill",
+ ):
+ locals()[method] = _simple_escaping_wrapper(method)
+
+ del method
+
+ def partition(self, sep: str) -> t.Tuple["Markup", "Markup", "Markup"]:
+ l, s, r = super().partition(self.escape(sep))
+ cls = self.__class__
+ return cls(l), cls(s), cls(r)
+
+ def rpartition(self, sep: str) -> t.Tuple["Markup", "Markup", "Markup"]:
+ l, s, r = super().rpartition(self.escape(sep))
+ cls = self.__class__
+ return cls(l), cls(s), cls(r)
+
+ def format(self, *args: t.Any, **kwargs: t.Any) -> "Markup":
+ formatter = EscapeFormatter(self.escape)
+ return self.__class__(formatter.vformat(self, args, kwargs))
+
+ def __html_format__(self, format_spec: str) -> "Markup":
+ if format_spec:
+ raise ValueError("Unsupported format specification for Markup.")
+
+ return self
+
+
+class EscapeFormatter(string.Formatter):
+ __slots__ = ("escape",)
+
+ def __init__(self, escape: t.Callable[[t.Any], Markup]) -> None:
+ self.escape = escape
+ super().__init__()
+
+ def format_field(self, value: t.Any, format_spec: str) -> str:
+ if hasattr(value, "__html_format__"):
+ rv = value.__html_format__(format_spec)
+ elif hasattr(value, "__html__"):
+ if format_spec:
+ raise ValueError(
+ f"Format specifier {format_spec} given, but {type(value)} does not"
+ " define __html_format__. A class that defines __html__ must define"
+ " __html_format__ to work with format specifiers."
+ )
+ rv = value.__html__()
+ else:
+ # We need to make sure the format spec is str here as
+ # otherwise the wrong callback methods are invoked.
+ rv = string.Formatter.format_field(self, value, str(format_spec))
+ return str(self.escape(rv))
+
+
+_ListOrDict = t.TypeVar("_ListOrDict", list, dict)
+
+
+def _escape_argspec(
+ obj: _ListOrDict, iterable: t.Iterable[t.Any], escape: t.Callable[[t.Any], Markup]
+) -> _ListOrDict:
+ """Helper for various string-wrapped functions."""
+ for key, value in iterable:
+ if isinstance(value, str) or hasattr(value, "__html__"):
+ obj[key] = escape(value)
+
+ return obj
+
+
+class _MarkupEscapeHelper:
+ """Helper for :meth:`Markup.__mod__`."""
+
+ __slots__ = ("obj", "escape")
+
+ def __init__(self, obj: t.Any, escape: t.Callable[[t.Any], Markup]) -> None:
+ self.obj = obj
+ self.escape = escape
+
+ def __getitem__(self, item: t.Any) -> "_MarkupEscapeHelper":
+ return _MarkupEscapeHelper(self.obj[item], self.escape)
+
+ def __str__(self) -> str:
+ return str(self.escape(self.obj))
+
+ def __repr__(self) -> str:
+ return str(self.escape(repr(self.obj)))
+
+ def __int__(self) -> int:
+ return int(self.obj)
+
+ def __float__(self) -> float:
+ return float(self.obj)
+
+
+# circular import
+try:
+ from ._speedups import escape as escape
+ from ._speedups import escape_silent as escape_silent
+ from ._speedups import soft_str as soft_str
+ from ._speedups import soft_unicode
+except ImportError:
+ from ._native import escape as escape
+ from ._native import escape_silent as escape_silent # noqa: F401
+ from ._native import soft_str as soft_str # noqa: F401
+ from ._native import soft_unicode # noqa: F401
diff --git a/third_party/python/MarkupSafe/src/markupsafe/_native.py b/third_party/python/MarkupSafe/src/markupsafe/_native.py
new file mode 100644
index 0000000000..6f7eb7a8cb
--- /dev/null
+++ b/third_party/python/MarkupSafe/src/markupsafe/_native.py
@@ -0,0 +1,75 @@
+import typing as t
+
+from . import Markup
+
+
+def escape(s: t.Any) -> Markup:
+ """Replace the characters ``&``, ``<``, ``>``, ``'``, and ``"`` in
+ the string with HTML-safe sequences. Use this if you need to display
+ text that might contain such characters in HTML.
+
+ If the object has an ``__html__`` method, it is called and the
+ return value is assumed to already be safe for HTML.
+
+ :param s: An object to be converted to a string and escaped.
+ :return: A :class:`Markup` string with the escaped text.
+ """
+ if hasattr(s, "__html__"):
+ return Markup(s.__html__())
+
+ return Markup(
+ str(s)
+ .replace("&", "&amp;")
+ .replace(">", "&gt;")
+ .replace("<", "&lt;")
+ .replace("'", "&#39;")
+ .replace('"', "&#34;")
+ )
+
+
+def escape_silent(s: t.Optional[t.Any]) -> Markup:
+ """Like :func:`escape` but treats ``None`` as the empty string.
+ Useful with optional values, as otherwise you get the string
+ ``'None'`` when the value is ``None``.
+
+ >>> escape(None)
+ Markup('None')
+ >>> escape_silent(None)
+ Markup('')
+ """
+ if s is None:
+ return Markup()
+
+ return escape(s)
+
+
+def soft_str(s: t.Any) -> str:
+ """Convert an object to a string if it isn't already. This preserves
+ a :class:`Markup` string rather than converting it back to a basic
+ string, so it will still be marked as safe and won't be escaped
+ again.
+
+ >>> value = escape("<User 1>")
+ >>> value
+ Markup('&lt;User 1&gt;')
+ >>> escape(str(value))
+ Markup('&amp;lt;User 1&amp;gt;')
+ >>> escape(soft_str(value))
+ Markup('&lt;User 1&gt;')
+ """
+ if not isinstance(s, str):
+ return str(s)
+
+ return s
+
+
+def soft_unicode(s: t.Any) -> str:
+ import warnings
+
+ warnings.warn(
+ "'soft_unicode' has been renamed to 'soft_str'. The old name"
+ " will be removed in MarkupSafe 2.1.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return soft_str(s)
diff --git a/third_party/python/MarkupSafe/src/markupsafe/_speedups.c b/third_party/python/MarkupSafe/src/markupsafe/_speedups.c
new file mode 100644
index 0000000000..44967b1fdc
--- /dev/null
+++ b/third_party/python/MarkupSafe/src/markupsafe/_speedups.c
@@ -0,0 +1,339 @@
+#include <Python.h>
+
+static PyObject* markup;
+
+static int
+init_constants(void)
+{
+ PyObject *module;
+
+ /* import markup type so that we can mark the return value */
+ module = PyImport_ImportModule("markupsafe");
+ if (!module)
+ return 0;
+ markup = PyObject_GetAttrString(module, "Markup");
+ Py_DECREF(module);
+
+ return 1;
+}
+
+#define GET_DELTA(inp, inp_end, delta) \
+ while (inp < inp_end) { \
+ switch (*inp++) { \
+ case '"': \
+ case '\'': \
+ case '&': \
+ delta += 4; \
+ break; \
+ case '<': \
+ case '>': \
+ delta += 3; \
+ break; \
+ } \
+ }
+
+#define DO_ESCAPE(inp, inp_end, outp) \
+ { \
+ Py_ssize_t ncopy = 0; \
+ while (inp < inp_end) { \
+ switch (*inp) { \
+ case '"': \
+ memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
+ outp += ncopy; ncopy = 0; \
+ *outp++ = '&'; \
+ *outp++ = '#'; \
+ *outp++ = '3'; \
+ *outp++ = '4'; \
+ *outp++ = ';'; \
+ break; \
+ case '\'': \
+ memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
+ outp += ncopy; ncopy = 0; \
+ *outp++ = '&'; \
+ *outp++ = '#'; \
+ *outp++ = '3'; \
+ *outp++ = '9'; \
+ *outp++ = ';'; \
+ break; \
+ case '&': \
+ memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
+ outp += ncopy; ncopy = 0; \
+ *outp++ = '&'; \
+ *outp++ = 'a'; \
+ *outp++ = 'm'; \
+ *outp++ = 'p'; \
+ *outp++ = ';'; \
+ break; \
+ case '<': \
+ memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
+ outp += ncopy; ncopy = 0; \
+ *outp++ = '&'; \
+ *outp++ = 'l'; \
+ *outp++ = 't'; \
+ *outp++ = ';'; \
+ break; \
+ case '>': \
+ memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
+ outp += ncopy; ncopy = 0; \
+ *outp++ = '&'; \
+ *outp++ = 'g'; \
+ *outp++ = 't'; \
+ *outp++ = ';'; \
+ break; \
+ default: \
+ ncopy++; \
+ } \
+ inp++; \
+ } \
+ memcpy(outp, inp-ncopy, sizeof(*outp)*ncopy); \
+ }
+
+static PyObject*
+escape_unicode_kind1(PyUnicodeObject *in)
+{
+ Py_UCS1 *inp = PyUnicode_1BYTE_DATA(in);
+ Py_UCS1 *inp_end = inp + PyUnicode_GET_LENGTH(in);
+ Py_UCS1 *outp;
+ PyObject *out;
+ Py_ssize_t delta = 0;
+
+ GET_DELTA(inp, inp_end, delta);
+ if (!delta) {
+ Py_INCREF(in);
+ return (PyObject*)in;
+ }
+
+ out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta,
+ PyUnicode_IS_ASCII(in) ? 127 : 255);
+ if (!out)
+ return NULL;
+
+ inp = PyUnicode_1BYTE_DATA(in);
+ outp = PyUnicode_1BYTE_DATA(out);
+ DO_ESCAPE(inp, inp_end, outp);
+ return out;
+}
+
+static PyObject*
+escape_unicode_kind2(PyUnicodeObject *in)
+{
+ Py_UCS2 *inp = PyUnicode_2BYTE_DATA(in);
+ Py_UCS2 *inp_end = inp + PyUnicode_GET_LENGTH(in);
+ Py_UCS2 *outp;
+ PyObject *out;
+ Py_ssize_t delta = 0;
+
+ GET_DELTA(inp, inp_end, delta);
+ if (!delta) {
+ Py_INCREF(in);
+ return (PyObject*)in;
+ }
+
+ out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta, 65535);
+ if (!out)
+ return NULL;
+
+ inp = PyUnicode_2BYTE_DATA(in);
+ outp = PyUnicode_2BYTE_DATA(out);
+ DO_ESCAPE(inp, inp_end, outp);
+ return out;
+}
+
+
+static PyObject*
+escape_unicode_kind4(PyUnicodeObject *in)
+{
+ Py_UCS4 *inp = PyUnicode_4BYTE_DATA(in);
+ Py_UCS4 *inp_end = inp + PyUnicode_GET_LENGTH(in);
+ Py_UCS4 *outp;
+ PyObject *out;
+ Py_ssize_t delta = 0;
+
+ GET_DELTA(inp, inp_end, delta);
+ if (!delta) {
+ Py_INCREF(in);
+ return (PyObject*)in;
+ }
+
+ out = PyUnicode_New(PyUnicode_GET_LENGTH(in) + delta, 1114111);
+ if (!out)
+ return NULL;
+
+ inp = PyUnicode_4BYTE_DATA(in);
+ outp = PyUnicode_4BYTE_DATA(out);
+ DO_ESCAPE(inp, inp_end, outp);
+ return out;
+}
+
+static PyObject*
+escape_unicode(PyUnicodeObject *in)
+{
+ if (PyUnicode_READY(in))
+ return NULL;
+
+ switch (PyUnicode_KIND(in)) {
+ case PyUnicode_1BYTE_KIND:
+ return escape_unicode_kind1(in);
+ case PyUnicode_2BYTE_KIND:
+ return escape_unicode_kind2(in);
+ case PyUnicode_4BYTE_KIND:
+ return escape_unicode_kind4(in);
+ }
+ assert(0); /* shouldn't happen */
+ return NULL;
+}
+
+static PyObject*
+escape(PyObject *self, PyObject *text)
+{
+ static PyObject *id_html;
+ PyObject *s = NULL, *rv = NULL, *html;
+
+ if (id_html == NULL) {
+ id_html = PyUnicode_InternFromString("__html__");
+ if (id_html == NULL) {
+ return NULL;
+ }
+ }
+
+ /* we don't have to escape integers, bools or floats */
+ if (PyLong_CheckExact(text) ||
+ PyFloat_CheckExact(text) || PyBool_Check(text) ||
+ text == Py_None)
+ return PyObject_CallFunctionObjArgs(markup, text, NULL);
+
+ /* if the object has an __html__ method that performs the escaping */
+ html = PyObject_GetAttr(text ,id_html);
+ if (html) {
+ s = PyObject_CallObject(html, NULL);
+ Py_DECREF(html);
+ if (s == NULL) {
+ return NULL;
+ }
+ /* Convert to Markup object */
+ rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL);
+ Py_DECREF(s);
+ return rv;
+ }
+
+ /* otherwise make the object unicode if it isn't, then escape */
+ PyErr_Clear();
+ if (!PyUnicode_Check(text)) {
+ PyObject *unicode = PyObject_Str(text);
+ if (!unicode)
+ return NULL;
+ s = escape_unicode((PyUnicodeObject*)unicode);
+ Py_DECREF(unicode);
+ }
+ else
+ s = escape_unicode((PyUnicodeObject*)text);
+
+ /* convert the unicode string into a markup object. */
+ rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL);
+ Py_DECREF(s);
+ return rv;
+}
+
+
+static PyObject*
+escape_silent(PyObject *self, PyObject *text)
+{
+ if (text != Py_None)
+ return escape(self, text);
+ return PyObject_CallFunctionObjArgs(markup, NULL);
+}
+
+
+static PyObject*
+soft_str(PyObject *self, PyObject *s)
+{
+ if (!PyUnicode_Check(s))
+ return PyObject_Str(s);
+ Py_INCREF(s);
+ return s;
+}
+
+
+static PyObject*
+soft_unicode(PyObject *self, PyObject *s)
+{
+ PyErr_WarnEx(
+ PyExc_DeprecationWarning,
+ "'soft_unicode' has been renamed to 'soft_str'. The old name"
+ " will be removed in MarkupSafe 2.1.",
+ 2
+ );
+ return soft_str(self, s);
+}
+
+
+static PyMethodDef module_methods[] = {
+ {
+ "escape",
+ (PyCFunction)escape,
+ METH_O,
+ "Replace the characters ``&``, ``<``, ``>``, ``'``, and ``\"`` in"
+ " the string with HTML-safe sequences. Use this if you need to display"
+ " text that might contain such characters in HTML.\n\n"
+ "If the object has an ``__html__`` method, it is called and the"
+ " return value is assumed to already be safe for HTML.\n\n"
+ ":param s: An object to be converted to a string and escaped.\n"
+ ":return: A :class:`Markup` string with the escaped text.\n"
+ },
+ {
+ "escape_silent",
+ (PyCFunction)escape_silent,
+ METH_O,
+ "Like :func:`escape` but treats ``None`` as the empty string."
+ " Useful with optional values, as otherwise you get the string"
+ " ``'None'`` when the value is ``None``.\n\n"
+ ">>> escape(None)\n"
+ "Markup('None')\n"
+ ">>> escape_silent(None)\n"
+ "Markup('')\n"
+ },
+ {
+ "soft_str",
+ (PyCFunction)soft_str,
+ METH_O,
+ "Convert an object to a string if it isn't already. This preserves"
+ " a :class:`Markup` string rather than converting it back to a basic"
+ " string, so it will still be marked as safe and won't be escaped"
+ " again.\n\n"
+ ">>> value = escape(\"<User 1>\")\n"
+ ">>> value\n"
+ "Markup('&lt;User 1&gt;')\n"
+ ">>> escape(str(value))\n"
+ "Markup('&amp;lt;User 1&amp;gt;')\n"
+ ">>> escape(soft_str(value))\n"
+ "Markup('&lt;User 1&gt;')\n"
+ },
+ {
+ "soft_unicode",
+ (PyCFunction)soft_unicode,
+ METH_O,
+ ""
+ },
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+static struct PyModuleDef module_definition = {
+ PyModuleDef_HEAD_INIT,
+ "markupsafe._speedups",
+ NULL,
+ -1,
+ module_methods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+PyMODINIT_FUNC
+PyInit__speedups(void)
+{
+ if (!init_constants())
+ return NULL;
+
+ return PyModule_Create(&module_definition);
+}
diff --git a/third_party/python/MarkupSafe/src/markupsafe/_speedups.pyi b/third_party/python/MarkupSafe/src/markupsafe/_speedups.pyi
new file mode 100644
index 0000000000..f673240f6d
--- /dev/null
+++ b/third_party/python/MarkupSafe/src/markupsafe/_speedups.pyi
@@ -0,0 +1,9 @@
+from typing import Any
+from typing import Optional
+
+from . import Markup
+
+def escape(s: Any) -> Markup: ...
+def escape_silent(s: Optional[Any]) -> Markup: ...
+def soft_str(s: Any) -> str: ...
+def soft_unicode(s: Any) -> str: ...
diff --git a/third_party/python/MarkupSafe/src/markupsafe/py.typed b/third_party/python/MarkupSafe/src/markupsafe/py.typed
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/third_party/python/MarkupSafe/src/markupsafe/py.typed
diff --git a/third_party/python/MarkupSafe/tox.ini b/third_party/python/MarkupSafe/tox.ini
new file mode 100644
index 0000000000..de68730f28
--- /dev/null
+++ b/third_party/python/MarkupSafe/tox.ini
@@ -0,0 +1,24 @@
+[tox]
+envlist =
+ py{39,38,37,36,py3}
+ style
+ typing
+ docs
+skip_missing_interpreters = true
+
+[testenv]
+deps = -r requirements/tests.txt
+commands = pytest -v --tb=short --basetemp={envtmpdir} {posargs}
+
+[testenv:style]
+deps = pre-commit
+skip_install = true
+commands = pre-commit run --all-files --show-diff-on-failure
+
+[testenv:typing]
+deps = -r requirements/typing.txt
+commands = mypy
+
+[testenv:docs]
+deps = -r requirements/docs.txt
+commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html