summaryrefslogtreecommitdiffstats
path: root/third_party/python/multidict
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/python/multidict')
-rw-r--r--third_party/python/multidict/CHANGES.rst255
-rw-r--r--third_party/python/multidict/LICENSE201
-rw-r--r--third_party/python/multidict/MANIFEST.in14
-rw-r--r--third_party/python/multidict/Makefile108
-rw-r--r--third_party/python/multidict/PKG-INFO128
-rw-r--r--third_party/python/multidict/README.rst103
-rw-r--r--third_party/python/multidict/multidict.egg-info/PKG-INFO128
-rw-r--r--third_party/python/multidict/multidict.egg-info/SOURCES.txt71
-rw-r--r--third_party/python/multidict/multidict.egg-info/dependency_links.txt1
-rw-r--r--third_party/python/multidict/multidict.egg-info/top_level.txt1
-rw-r--r--third_party/python/multidict/multidict/__init__.py48
-rw-r--r--third_party/python/multidict/multidict/__init__.pyi152
-rw-r--r--third_party/python/multidict/multidict/_abc.py48
-rw-r--r--third_party/python/multidict/multidict/_compat.py14
-rw-r--r--third_party/python/multidict/multidict/_multidict.c1646
-rw-r--r--third_party/python/multidict/multidict/_multidict_base.py144
-rw-r--r--third_party/python/multidict/multidict/_multidict_py.py515
-rw-r--r--third_party/python/multidict/multidict/_multilib/defs.h22
-rw-r--r--third_party/python/multidict/multidict/_multilib/dict.h24
-rw-r--r--third_party/python/multidict/multidict/_multilib/istr.h85
-rw-r--r--third_party/python/multidict/multidict/_multilib/iter.h238
-rw-r--r--third_party/python/multidict/multidict/_multilib/pair_list.h1244
-rw-r--r--third_party/python/multidict/multidict/_multilib/views.h464
-rw-r--r--third_party/python/multidict/multidict/py.typed1
-rw-r--r--third_party/python/multidict/pyproject.toml11
-rw-r--r--third_party/python/multidict/setup.cfg37
-rw-r--r--third_party/python/multidict/setup.py96
27 files changed, 5799 insertions, 0 deletions
diff --git a/third_party/python/multidict/CHANGES.rst b/third_party/python/multidict/CHANGES.rst
new file mode 100644
index 0000000000..c10b8c297a
--- /dev/null
+++ b/third_party/python/multidict/CHANGES.rst
@@ -0,0 +1,255 @@
+=========
+Changelog
+=========
+
+..
+ You should *NOT* be adding new change log entries to this file, this
+ file is managed by towncrier. You *may* edit previous change logs to
+ fix problems like typo corrections or such.
+ To add a new change log entry, please see
+ https://pip.pypa.io/en/latest/development/#adding-a-news-entry
+ we named the news folder "changes".
+
+ WARNING: Don't drop the next directive!
+
+.. towncrier release notes start
+
+5.1.0 (2020-12-03)
+==================
+
+Features
+--------
+
+- Support ``GenericAliases`` (``MultiDict[str]``) for Python 3.9+
+ `#553 <https://github.com/aio-libs/multidict/issues/553>`_
+
+
+Bugfixes
+--------
+
+- Synchronize the declared supported Python versions in ``setup.py`` with actually supported and tested ones.
+ `#552 <https://github.com/aio-libs/multidict/issues/552>`_
+
+
+----
+
+
+5.0.1 (2020-11-14)
+==================
+
+Bugfixes
+--------
+
+- Provide x86 Windows wheels
+ `#550 <https://github.com/aio-libs/multidict/issues/550>`_
+
+
+----
+
+
+5.0.0 (2020-10-12)
+==================
+
+Features
+--------
+
+- Provide wheels for ``aarch64``, ``i686``, ``ppc64le``, ``s390x`` architectures on Linux
+ as well as ``x86_64``.
+ `#500 <https://github.com/aio-libs/multidict/issues/500>`_
+- Provide wheels for Python 3.9.
+ `#534 <https://github.com/aio-libs/multidict/issues/534>`_
+
+Removal
+-------
+
+- Drop Python 3.5 support; Python 3.6 is the minimal supported Python version.
+
+Misc
+----
+
+- `#503 <https://github.com/aio-libs/multidict/issues/503>`_
+
+
+----
+
+
+4.7.6 (2020-05-15)
+==================
+
+Bugfixes
+--------
+
+- Fixed an issue with some versions of the ``wheel`` dist
+ failing because of being unable to detect the license file.
+ `#481 <https://github.com/aio-libs/multidict/issues/481>`_
+
+
+----
+
+
+4.7.5 (2020-02-21)
+==================
+
+Bugfixes
+--------
+
+- Fixed creating and updating of MultiDict from a sequence of pairs and keyword
+ arguments. Previously passing a list argument modified it inplace, and other sequences
+ caused an error.
+ `#457 <https://github.com/aio-libs/multidict/issues/457>`_
+- Fixed comparing with mapping: an exception raised in the
+ :py:func:`~object.__len__` method caused raising a SyntaxError.
+ `#459 <https://github.com/aio-libs/multidict/issues/459>`_
+- Fixed comparing with mapping: all exceptions raised in the
+ :py:func:`~object.__getitem__` method were silenced.
+ `#460 <https://github.com/aio-libs/multidict/issues/460>`_
+
+
+----
+
+
+4.7.4 (2020-01-11)
+==================
+
+Bugfixes
+--------
+
+- ``MultiDict.iter`` fix memory leak when used iterator over
+ :py:mod:`multidict` instance.
+ `#452 <https://github.com/aio-libs/multidict/issues/452>`_
+
+
+----
+
+
+4.7.3 (2019-12-30)
+==================
+
+Features
+--------
+
+- Implement ``__sizeof__`` function to correctly calculate all internal structures size.
+ `#444 <https://github.com/aio-libs/multidict/issues/444>`_
+- Expose ``getversion()`` function.
+ `#451 <https://github.com/aio-libs/multidict/issues/451>`_
+
+
+Bugfixes
+--------
+
+- Fix crashes in ``popone``/``popall`` when default is returned.
+ `#450 <https://github.com/aio-libs/multidict/issues/450>`_
+
+
+Improved Documentation
+----------------------
+
+- Corrected the documentation for ``MultiDict.extend()``
+ `#446 <https://github.com/aio-libs/multidict/issues/446>`_
+
+
+----
+
+
+4.7.2 (2019-12-20)
+==================
+
+Bugfixes
+--------
+
+- Fix crashing when multidict is used pyinstaller
+ `#432 <https://github.com/aio-libs/multidict/issues/432>`_
+- Fix typing for :py:meth:`CIMultiDict.copy`
+ `#434 <https://github.com/aio-libs/multidict/issues/434>`_
+- Fix memory leak in ``MultiDict.copy()``
+ `#443 <https://github.com/aio-libs/multidict/issues/443>`_
+
+
+----
+
+
+4.7.1 (2019-12-12)
+==================
+
+Bugfixes
+--------
+
+- :py:meth:`CIMultiDictProxy.copy` return object type
+ :py:class:`multidict._multidict.CIMultiDict`
+ `#427 <https://github.com/aio-libs/multidict/issues/427>`_
+- Make :py:class:`CIMultiDict` subclassable again
+ `#416 <https://github.com/aio-libs/multidict/issues/416>`_
+- Fix regression, multidict can be constructed from arbitrary iterable of pairs again.
+ `#418 <https://github.com/aio-libs/multidict/issues/418>`_
+- :py:meth:`CIMultiDict.add` may be called with keyword arguments
+ `#421 <https://github.com/aio-libs/multidict/issues/421>`_
+
+
+Improved Documentation
+----------------------
+
+- Mention ``MULTIDICT_NO_EXTENSIONS`` environment variable in docs.
+ `#393 <https://github.com/aio-libs/multidict/issues/393>`_
+- Document the fact that ``istr`` preserves the casing of argument untouched but uses internal lower-cased copy for keys comparison.
+ `#419 <https://github.com/aio-libs/multidict/issues/419>`_
+
+
+----
+
+
+4.7.0 (2019-12-10)
+==================
+
+Features
+--------
+
+- Replace Cython optimization with pure C
+ `#249 <https://github.com/aio-libs/multidict/issues/249>`_
+- Implement ``__length_hint__()`` for iterators
+ `#310 <https://github.com/aio-libs/multidict/issues/310>`_
+- Support the MultiDict[str] generic specialization in the runtime.
+ `#392 <https://github.com/aio-libs/multidict/issues/392>`_
+- Embed pair_list_t structure into MultiDict Python object
+ `#395 <https://github.com/aio-libs/multidict/issues/395>`_
+- Embed multidict pairs for small dictionaries to amortize the memory usage.
+ `#396 <https://github.com/aio-libs/multidict/issues/396>`_
+- Support weak references to C Extension classes.
+ `#399 <https://github.com/aio-libs/multidict/issues/399>`_
+- Add docstrings to provided classes.
+ `#400 <https://github.com/aio-libs/multidict/issues/400>`_
+- Merge ``multidict._istr`` back with ``multidict._multidict``.
+ `#409 <https://github.com/aio-libs/multidict/issues/409>`_
+
+
+Bugfixes
+--------
+
+- Explicitly call ``tp_free`` slot on deallocation.
+ `#407 <https://github.com/aio-libs/multidict/issues/407>`_
+- Return class from __class_getitem__ to simplify subclassing
+ `#413 <https://github.com/aio-libs/multidict/issues/413>`_
+
+
+----
+
+
+4.6.1 (2019-11-21)
+====================
+
+Bugfixes
+--------
+
+- Fix PyPI link for GitHub Issues badge.
+ `#391 <https://github.com/aio-libs/aiohttp/issues/391>`_
+
+4.6.0 (2019-11-20)
+====================
+
+Bugfixes
+--------
+
+- Fix GC object tracking.
+ `#314 <https://github.com/aio-libs/aiohttp/issues/314>`_
+- Preserve the case of `istr` strings.
+ `#374 <https://github.com/aio-libs/aiohttp/issues/374>`_
+- Generate binary wheels for Python 3.8.
diff --git a/third_party/python/multidict/LICENSE b/third_party/python/multidict/LICENSE
new file mode 100644
index 0000000000..99a9e21af0
--- /dev/null
+++ b/third_party/python/multidict/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2016-2017 Andrew Svetlov
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/third_party/python/multidict/MANIFEST.in b/third_party/python/multidict/MANIFEST.in
new file mode 100644
index 0000000000..c39a12f0b6
--- /dev/null
+++ b/third_party/python/multidict/MANIFEST.in
@@ -0,0 +1,14 @@
+include LICENSE
+include CHANGES.rst
+include README.rst
+include Makefile
+graft multidict
+graft docs
+graft tests
+global-exclude *.pyc
+include multidict/*.c
+exclude multidict/_multidict.html
+exclude multidict/*.so
+exclude multidict/*.pyd
+exclude multidict/*.pyd
+prune docs/_build
diff --git a/third_party/python/multidict/Makefile b/third_party/python/multidict/Makefile
new file mode 100644
index 0000000000..ca0562b043
--- /dev/null
+++ b/third_party/python/multidict/Makefile
@@ -0,0 +1,108 @@
+# Some simple testing tasks (sorry, UNIX only).
+.PHONY: all build flake test vtest cov clean doc mypy
+
+
+PYXS = $(wildcard multidict/*.pyx)
+SRC = multidict tests setup.py
+
+all: test
+
+.install-deps: $(shell find requirements -type f)
+ pip install -r requirements/dev.txt
+ @touch .install-deps
+
+.flake: .install-deps $(shell find multidict -type f) \
+ $(shell find tests -type f)
+ flake8 multidict tests
+ @if ! isort --check multidict tests; then \
+ echo "Import sort errors, run 'make fmt' to fix them!!!"; \
+ isort --diff --check multidict tests; \
+ false; \
+ fi
+ @touch .flake
+
+
+isort-check:
+ @if ! isort --check $(SRC); then \
+ echo "Import sort errors, run 'make fmt' to fix them!!!"; \
+ isort --diff --check $(SRC); \
+ false; \
+ fi
+
+flake8:
+ flake8 $(SRC)
+
+black-check:
+ @if ! isort --check $(SRC); then \
+ echo "black errors, run 'make fmt' to fix them!!!"; \
+ black -t py35 --diff --check $(SRC); \
+ false; \
+ fi
+
+mypy:
+ mypy --show-error-codes multidict tests
+
+lint: flake8 black-check mypy isort-check check_changes
+
+fmt:
+ black -t py35 $(SRC)
+ isort $(SRC)
+
+check_changes:
+ ./tools/check_changes.py
+
+.develop: .install-deps $(shell find multidict -type f) .flake check_changes mypy
+ pip install -e .
+ @touch .develop
+
+test: .develop
+ @pytest -q
+
+vtest: .develop
+ @pytest -s -v
+
+cov-dev: .develop
+ @pytest --cov-report=html
+ @echo "open file://`pwd`/htmlcov/index.html"
+
+cov-ci-run: .develop
+ @echo "Regular run"
+ @pytest --cov-report=html
+
+cov-dev-full: cov-ci-run
+ @echo "open file://`pwd`/htmlcov/index.html"
+
+doc:
+ @make -C docs html SPHINXOPTS="-W -E"
+ @echo "open file://`pwd`/docs/_build/html/index.html"
+
+doc-spelling:
+ @make -C docs spelling SPHINXOPTS="-W -E"
+
+install:
+ @pip install -U 'pip'
+ @pip install -Ur requirements/dev.txt
+
+install-dev: .develop
+
+
+clean:
+ rm -rf `find . -name __pycache__`
+ rm -f `find . -type f -name '*.py[co]' `
+ rm -f `find . -type f -name '*~' `
+ rm -f `find . -type f -name '.*~' `
+ rm -f `find . -type f -name '@*' `
+ rm -f `find . -type f -name '#*#' `
+ rm -f `find . -type f -name '*.orig' `
+ rm -f `find . -type f -name '*.rej' `
+ rm -f .coverage
+ rm -rf coverage
+ rm -rf build
+ rm -rf cover
+ rm -rf htmlcov
+ make -C docs clean SPHINXBUILD=false
+ python3 setup.py clean
+ rm -f multidict/*.html
+ rm -f multidict/*.so
+ rm -f multidict/*.pyd
+ rm -rf .tox
diff --git a/third_party/python/multidict/PKG-INFO b/third_party/python/multidict/PKG-INFO
new file mode 100644
index 0000000000..bbd4864947
--- /dev/null
+++ b/third_party/python/multidict/PKG-INFO
@@ -0,0 +1,128 @@
+Metadata-Version: 1.2
+Name: multidict
+Version: 5.1.0
+Summary: multidict implementation
+Home-page: https://github.com/aio-libs/multidict
+Author: Andrew Svetlov
+Author-email: andrew.svetlov@gmail.com
+License: Apache 2
+Project-URL: Chat: Gitter, https://gitter.im/aio-libs/Lobby
+Project-URL: CI: Azure Pipelines, https://dev.azure.com/aio-libs/multidict/_build
+Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/multidict
+Project-URL: Docs: RTD, https://multidict.readthedocs.io
+Project-URL: GitHub: issues, https://github.com/aio-libs/multidict/issues
+Project-URL: GitHub: repo, https://github.com/aio-libs/multidict
+Description: =========
+ multidict
+ =========
+
+ .. image:: https://github.com/aio-libs/multidict/workflows/CI/badge.svg
+ :target: https://github.com/aio-libs/multidict/actions?query=workflow%3ACI
+ :alt: GitHub status for master branch
+
+ .. image:: https://codecov.io/gh/aio-libs/multidict/branch/master/graph/badge.svg
+ :target: https://codecov.io/gh/aio-libs/multidict
+ :alt: Coverage metrics
+
+ .. image:: https://img.shields.io/pypi/v/multidict.svg
+ :target: https://pypi.org/project/multidict
+ :alt: PyPI
+
+ .. image:: https://readthedocs.org/projects/multidict/badge/?version=latest
+ :target: http://multidict.readthedocs.org/en/latest/?badge=latest
+ :alt: Documentationb
+
+ .. image:: https://img.shields.io/pypi/pyversions/multidict.svg
+ :target: https://pypi.org/project/multidict
+ :alt: Python versions
+
+ .. image:: https://badges.gitter.im/Join%20Chat.svg
+ :target: https://gitter.im/aio-libs/Lobby
+ :alt: Chat on Gitter
+
+ Multidict is dict-like collection of *key-value pairs* where key
+ might be occurred more than once in the container.
+
+ Introduction
+ ------------
+
+ *HTTP Headers* and *URL query string* require specific data structure:
+ *multidict*. It behaves mostly like a regular ``dict`` but it may have
+ several *values* for the same *key* and *preserves insertion ordering*.
+
+ The *key* is ``str`` (or ``istr`` for case-insensitive dictionaries).
+
+ ``multidict`` has four multidict classes:
+ ``MultiDict``, ``MultiDictProxy``, ``CIMultiDict``
+ and ``CIMultiDictProxy``.
+
+ Immutable proxies (``MultiDictProxy`` and
+ ``CIMultiDictProxy``) provide a dynamic view for the
+ proxied multidict, the view reflects underlying collection changes. They
+ implement the ``collections.abc.Mapping`` interface.
+
+ Regular mutable (``MultiDict`` and ``CIMultiDict``) classes
+ implement ``collections.abc.MutableMapping`` and allows to change
+ their own content.
+
+
+ *Case insensitive* (``CIMultiDict`` and
+ ``CIMultiDictProxy``) ones assume the *keys* are case
+ insensitive, e.g.::
+
+ >>> dct = CIMultiDict(key='val')
+ >>> 'Key' in dct
+ True
+ >>> dct['Key']
+ 'val'
+
+ *Keys* should be ``str`` or ``istr`` instances.
+
+ The library has optional C Extensions for sake of speed.
+
+
+ License
+ -------
+
+ Apache 2
+
+ Library Installation
+ --------------------
+
+ .. code-block:: bash
+
+ $ pip install multidict
+
+ The library is Python 3 only!
+
+ PyPI contains binary wheels for Linux, Windows and MacOS. If you want to install
+ ``multidict`` on another operation system (or *Alpine Linux* inside a Docker) the
+ Tarball will be used to compile the library from sources. It requires C compiler and
+ Python headers installed.
+
+ To skip the compilation please use `MULTIDICT_NO_EXTENSIONS` environment variable,
+ e.g.:
+
+ .. code-block:: bash
+
+ $ MULTIDICT_NO_EXTENSIONS=1 pip install multidict
+
+ Please note, Pure Python (uncompiled) version is about 20-50 times slower depending on
+ the usage scenario!!!
+
+
+
+ Changelog
+ ---------
+ See `RTD page <http://multidict.readthedocs.org/en/latest/changes.html>`_.
+Platform: UNKNOWN
+Classifier: License :: OSI Approved :: Apache Software License
+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 :: 3.9
+Classifier: Development Status :: 5 - Production/Stable
+Requires-Python: >=3.6
diff --git a/third_party/python/multidict/README.rst b/third_party/python/multidict/README.rst
new file mode 100644
index 0000000000..e78e5065c2
--- /dev/null
+++ b/third_party/python/multidict/README.rst
@@ -0,0 +1,103 @@
+=========
+multidict
+=========
+
+.. image:: https://github.com/aio-libs/multidict/workflows/CI/badge.svg
+ :target: https://github.com/aio-libs/multidict/actions?query=workflow%3ACI
+ :alt: GitHub status for master branch
+
+.. image:: https://codecov.io/gh/aio-libs/multidict/branch/master/graph/badge.svg
+ :target: https://codecov.io/gh/aio-libs/multidict
+ :alt: Coverage metrics
+
+.. image:: https://img.shields.io/pypi/v/multidict.svg
+ :target: https://pypi.org/project/multidict
+ :alt: PyPI
+
+.. image:: https://readthedocs.org/projects/multidict/badge/?version=latest
+ :target: http://multidict.readthedocs.org/en/latest/?badge=latest
+ :alt: Documentationb
+
+.. image:: https://img.shields.io/pypi/pyversions/multidict.svg
+ :target: https://pypi.org/project/multidict
+ :alt: Python versions
+
+.. image:: https://badges.gitter.im/Join%20Chat.svg
+ :target: https://gitter.im/aio-libs/Lobby
+ :alt: Chat on Gitter
+
+Multidict is dict-like collection of *key-value pairs* where key
+might be occurred more than once in the container.
+
+Introduction
+------------
+
+*HTTP Headers* and *URL query string* require specific data structure:
+*multidict*. It behaves mostly like a regular ``dict`` but it may have
+several *values* for the same *key* and *preserves insertion ordering*.
+
+The *key* is ``str`` (or ``istr`` for case-insensitive dictionaries).
+
+``multidict`` has four multidict classes:
+``MultiDict``, ``MultiDictProxy``, ``CIMultiDict``
+and ``CIMultiDictProxy``.
+
+Immutable proxies (``MultiDictProxy`` and
+``CIMultiDictProxy``) provide a dynamic view for the
+proxied multidict, the view reflects underlying collection changes. They
+implement the ``collections.abc.Mapping`` interface.
+
+Regular mutable (``MultiDict`` and ``CIMultiDict``) classes
+implement ``collections.abc.MutableMapping`` and allows to change
+their own content.
+
+
+*Case insensitive* (``CIMultiDict`` and
+``CIMultiDictProxy``) ones assume the *keys* are case
+insensitive, e.g.::
+
+ >>> dct = CIMultiDict(key='val')
+ >>> 'Key' in dct
+ True
+ >>> dct['Key']
+ 'val'
+
+*Keys* should be ``str`` or ``istr`` instances.
+
+The library has optional C Extensions for sake of speed.
+
+
+License
+-------
+
+Apache 2
+
+Library Installation
+--------------------
+
+.. code-block:: bash
+
+ $ pip install multidict
+
+The library is Python 3 only!
+
+PyPI contains binary wheels for Linux, Windows and MacOS. If you want to install
+``multidict`` on another operation system (or *Alpine Linux* inside a Docker) the
+Tarball will be used to compile the library from sources. It requires C compiler and
+Python headers installed.
+
+To skip the compilation please use `MULTIDICT_NO_EXTENSIONS` environment variable,
+e.g.:
+
+.. code-block:: bash
+
+ $ MULTIDICT_NO_EXTENSIONS=1 pip install multidict
+
+Please note, Pure Python (uncompiled) version is about 20-50 times slower depending on
+the usage scenario!!!
+
+
+
+Changelog
+---------
+See `RTD page <http://multidict.readthedocs.org/en/latest/changes.html>`_.
diff --git a/third_party/python/multidict/multidict.egg-info/PKG-INFO b/third_party/python/multidict/multidict.egg-info/PKG-INFO
new file mode 100644
index 0000000000..bbd4864947
--- /dev/null
+++ b/third_party/python/multidict/multidict.egg-info/PKG-INFO
@@ -0,0 +1,128 @@
+Metadata-Version: 1.2
+Name: multidict
+Version: 5.1.0
+Summary: multidict implementation
+Home-page: https://github.com/aio-libs/multidict
+Author: Andrew Svetlov
+Author-email: andrew.svetlov@gmail.com
+License: Apache 2
+Project-URL: Chat: Gitter, https://gitter.im/aio-libs/Lobby
+Project-URL: CI: Azure Pipelines, https://dev.azure.com/aio-libs/multidict/_build
+Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/multidict
+Project-URL: Docs: RTD, https://multidict.readthedocs.io
+Project-URL: GitHub: issues, https://github.com/aio-libs/multidict/issues
+Project-URL: GitHub: repo, https://github.com/aio-libs/multidict
+Description: =========
+ multidict
+ =========
+
+ .. image:: https://github.com/aio-libs/multidict/workflows/CI/badge.svg
+ :target: https://github.com/aio-libs/multidict/actions?query=workflow%3ACI
+ :alt: GitHub status for master branch
+
+ .. image:: https://codecov.io/gh/aio-libs/multidict/branch/master/graph/badge.svg
+ :target: https://codecov.io/gh/aio-libs/multidict
+ :alt: Coverage metrics
+
+ .. image:: https://img.shields.io/pypi/v/multidict.svg
+ :target: https://pypi.org/project/multidict
+ :alt: PyPI
+
+ .. image:: https://readthedocs.org/projects/multidict/badge/?version=latest
+ :target: http://multidict.readthedocs.org/en/latest/?badge=latest
+ :alt: Documentationb
+
+ .. image:: https://img.shields.io/pypi/pyversions/multidict.svg
+ :target: https://pypi.org/project/multidict
+ :alt: Python versions
+
+ .. image:: https://badges.gitter.im/Join%20Chat.svg
+ :target: https://gitter.im/aio-libs/Lobby
+ :alt: Chat on Gitter
+
+ Multidict is dict-like collection of *key-value pairs* where key
+ might be occurred more than once in the container.
+
+ Introduction
+ ------------
+
+ *HTTP Headers* and *URL query string* require specific data structure:
+ *multidict*. It behaves mostly like a regular ``dict`` but it may have
+ several *values* for the same *key* and *preserves insertion ordering*.
+
+ The *key* is ``str`` (or ``istr`` for case-insensitive dictionaries).
+
+ ``multidict`` has four multidict classes:
+ ``MultiDict``, ``MultiDictProxy``, ``CIMultiDict``
+ and ``CIMultiDictProxy``.
+
+ Immutable proxies (``MultiDictProxy`` and
+ ``CIMultiDictProxy``) provide a dynamic view for the
+ proxied multidict, the view reflects underlying collection changes. They
+ implement the ``collections.abc.Mapping`` interface.
+
+ Regular mutable (``MultiDict`` and ``CIMultiDict``) classes
+ implement ``collections.abc.MutableMapping`` and allows to change
+ their own content.
+
+
+ *Case insensitive* (``CIMultiDict`` and
+ ``CIMultiDictProxy``) ones assume the *keys* are case
+ insensitive, e.g.::
+
+ >>> dct = CIMultiDict(key='val')
+ >>> 'Key' in dct
+ True
+ >>> dct['Key']
+ 'val'
+
+ *Keys* should be ``str`` or ``istr`` instances.
+
+ The library has optional C Extensions for sake of speed.
+
+
+ License
+ -------
+
+ Apache 2
+
+ Library Installation
+ --------------------
+
+ .. code-block:: bash
+
+ $ pip install multidict
+
+ The library is Python 3 only!
+
+ PyPI contains binary wheels for Linux, Windows and MacOS. If you want to install
+ ``multidict`` on another operation system (or *Alpine Linux* inside a Docker) the
+ Tarball will be used to compile the library from sources. It requires C compiler and
+ Python headers installed.
+
+ To skip the compilation please use `MULTIDICT_NO_EXTENSIONS` environment variable,
+ e.g.:
+
+ .. code-block:: bash
+
+ $ MULTIDICT_NO_EXTENSIONS=1 pip install multidict
+
+ Please note, Pure Python (uncompiled) version is about 20-50 times slower depending on
+ the usage scenario!!!
+
+
+
+ Changelog
+ ---------
+ See `RTD page <http://multidict.readthedocs.org/en/latest/changes.html>`_.
+Platform: UNKNOWN
+Classifier: License :: OSI Approved :: Apache Software License
+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 :: 3.9
+Classifier: Development Status :: 5 - Production/Stable
+Requires-Python: >=3.6
diff --git a/third_party/python/multidict/multidict.egg-info/SOURCES.txt b/third_party/python/multidict/multidict.egg-info/SOURCES.txt
new file mode 100644
index 0000000000..6c6257ea9b
--- /dev/null
+++ b/third_party/python/multidict/multidict.egg-info/SOURCES.txt
@@ -0,0 +1,71 @@
+CHANGES.rst
+LICENSE
+MANIFEST.in
+Makefile
+README.rst
+pyproject.toml
+setup.cfg
+setup.py
+docs/Makefile
+docs/benchmark.rst
+docs/changes.rst
+docs/conf.py
+docs/index.rst
+docs/make.bat
+docs/multidict.rst
+docs/spelling_wordlist.txt
+multidict/__init__.py
+multidict/__init__.pyi
+multidict/_abc.py
+multidict/_compat.py
+multidict/_multidict.c
+multidict/_multidict_base.py
+multidict/_multidict_py.py
+multidict/py.typed
+multidict.egg-info/PKG-INFO
+multidict.egg-info/SOURCES.txt
+multidict.egg-info/dependency_links.txt
+multidict.egg-info/top_level.txt
+multidict/_multilib/defs.h
+multidict/_multilib/dict.h
+multidict/_multilib/istr.h
+multidict/_multilib/iter.h
+multidict/_multilib/pair_list.h
+multidict/_multilib/views.h
+tests/cimultidict.pickle.0
+tests/cimultidict.pickle.1
+tests/cimultidict.pickle.2
+tests/cimultidict.pickle.3
+tests/cimultidict.pickle.4
+tests/cimultidict.pickle.5
+tests/conftest.py
+tests/gen_pickles.py
+tests/multidict.pickle.0
+tests/multidict.pickle.1
+tests/multidict.pickle.2
+tests/multidict.pickle.3
+tests/multidict.pickle.4
+tests/multidict.pickle.5
+tests/pycimultidict.pickle.0
+tests/pycimultidict.pickle.1
+tests/pycimultidict.pickle.2
+tests/pycimultidict.pickle.3
+tests/pycimultidict.pickle.4
+tests/pycimultidict.pickle.5
+tests/pymultidict.pickle.0
+tests/pymultidict.pickle.1
+tests/pymultidict.pickle.2
+tests/pymultidict.pickle.3
+tests/pymultidict.pickle.4
+tests/pymultidict.pickle.5
+tests/test_abc.py
+tests/test_copy.py
+tests/test_guard.py
+tests/test_istr.py
+tests/test_multidict.py
+tests/test_mutable_multidict.py
+tests/test_mypy.py
+tests/test_pickle.py
+tests/test_types.py
+tests/test_update.py
+tests/test_version.py \ No newline at end of file
diff --git a/third_party/python/multidict/multidict.egg-info/dependency_links.txt b/third_party/python/multidict/multidict.egg-info/dependency_links.txt
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/third_party/python/multidict/multidict.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/third_party/python/multidict/multidict.egg-info/top_level.txt b/third_party/python/multidict/multidict.egg-info/top_level.txt
new file mode 100644
index 0000000000..afcecdff08
--- /dev/null
+++ b/third_party/python/multidict/multidict.egg-info/top_level.txt
@@ -0,0 +1 @@
+multidict
diff --git a/third_party/python/multidict/multidict/__init__.py b/third_party/python/multidict/multidict/__init__.py
new file mode 100644
index 0000000000..6b091d1431
--- /dev/null
+++ b/third_party/python/multidict/multidict/__init__.py
@@ -0,0 +1,48 @@
+"""Multidict implementation.
+
+HTTP Headers and URL query string require specific data structure:
+multidict. It behaves mostly like a dict but it can have
+several values for the same key.
+"""
+
+from ._abc import MultiMapping, MutableMultiMapping
+from ._compat import USE_CYTHON_EXTENSIONS
+
+__all__ = (
+ "MultiMapping",
+ "MutableMultiMapping",
+ "MultiDictProxy",
+ "CIMultiDictProxy",
+ "MultiDict",
+ "CIMultiDict",
+ "upstr",
+ "istr",
+ "getversion",
+)
+
+__version__ = "5.1.0"
+
+
+try:
+ if not USE_CYTHON_EXTENSIONS:
+ raise ImportError
+ from ._multidict import (
+ CIMultiDict,
+ CIMultiDictProxy,
+ MultiDict,
+ MultiDictProxy,
+ getversion,
+ istr,
+ )
+except ImportError: # pragma: no cover
+ from ._multidict_py import (
+ CIMultiDict,
+ CIMultiDictProxy,
+ MultiDict,
+ MultiDictProxy,
+ getversion,
+ istr,
+ )
+
+
+upstr = istr
diff --git a/third_party/python/multidict/multidict/__init__.pyi b/third_party/python/multidict/multidict/__init__.pyi
new file mode 100644
index 0000000000..24ba63054b
--- /dev/null
+++ b/third_party/python/multidict/multidict/__init__.pyi
@@ -0,0 +1,152 @@
+import abc
+from typing import (
+ Dict,
+ Generic,
+ Iterable,
+ Iterator,
+ List,
+ Mapping,
+ MutableMapping,
+ Tuple,
+ TypeVar,
+ Union,
+ overload,
+)
+
+class istr(str): ...
+
+upstr = istr
+
+_S = Union[str, istr]
+
+_T = TypeVar("_T")
+
+_T_co = TypeVar("_T_co", covariant=True)
+
+_D = TypeVar("_D")
+
+class MultiMapping(Mapping[_S, _T_co]):
+ @overload
+ @abc.abstractmethod
+ def getall(self, key: _S) -> List[_T_co]: ...
+ @overload
+ @abc.abstractmethod
+ def getall(self, key: _S, default: _D) -> Union[List[_T_co], _D]: ...
+ @overload
+ @abc.abstractmethod
+ def getone(self, key: _S) -> _T_co: ...
+ @overload
+ @abc.abstractmethod
+ def getone(self, key: _S, default: _D) -> Union[_T_co, _D]: ...
+
+_Arg = Union[Mapping[_S, _T], Dict[_S, _T], MultiMapping[_T], Iterable[Tuple[_S, _T]]]
+
+class MutableMultiMapping(MultiMapping[_T], MutableMapping[_S, _T], Generic[_T]):
+ @abc.abstractmethod
+ def add(self, key: _S, value: _T) -> None: ...
+ @abc.abstractmethod
+ def extend(self, arg: _Arg[_T] = ..., **kwargs: _T) -> None: ...
+ @overload
+ @abc.abstractmethod
+ def popone(self, key: _S) -> _T: ...
+ @overload
+ @abc.abstractmethod
+ def popone(self, key: _S, default: _D) -> Union[_T, _D]: ...
+ @overload
+ @abc.abstractmethod
+ def popall(self, key: _S) -> List[_T]: ...
+ @overload
+ @abc.abstractmethod
+ def popall(self, key: _S, default: _D) -> Union[List[_T], _D]: ...
+
+class MultiDict(MutableMultiMapping[_T], Generic[_T]):
+ def __init__(self, arg: _Arg[_T] = ..., **kwargs: _T) -> None: ...
+ def copy(self) -> MultiDict[_T]: ...
+ def __getitem__(self, k: _S) -> _T: ...
+ def __setitem__(self, k: _S, v: _T) -> None: ...
+ def __delitem__(self, v: _S) -> None: ...
+ def __iter__(self) -> Iterator[_S]: ...
+ def __len__(self) -> int: ...
+ @overload
+ def getall(self, key: _S) -> List[_T]: ...
+ @overload
+ def getall(self, key: _S, default: _D) -> Union[List[_T], _D]: ...
+ @overload
+ def getone(self, key: _S) -> _T: ...
+ @overload
+ def getone(self, key: _S, default: _D) -> Union[_T, _D]: ...
+ def add(self, key: _S, value: _T) -> None: ...
+ def extend(self, arg: _Arg[_T] = ..., **kwargs: _T) -> None: ...
+ @overload
+ def popone(self, key: _S) -> _T: ...
+ @overload
+ def popone(self, key: _S, default: _D) -> Union[_T, _D]: ...
+ @overload
+ def popall(self, key: _S) -> List[_T]: ...
+ @overload
+ def popall(self, key: _S, default: _D) -> Union[List[_T], _D]: ...
+
+class CIMultiDict(MutableMultiMapping[_T], Generic[_T]):
+ def __init__(self, arg: _Arg[_T] = ..., **kwargs: _T) -> None: ...
+ def copy(self) -> CIMultiDict[_T]: ...
+ def __getitem__(self, k: _S) -> _T: ...
+ def __setitem__(self, k: _S, v: _T) -> None: ...
+ def __delitem__(self, v: _S) -> None: ...
+ def __iter__(self) -> Iterator[_S]: ...
+ def __len__(self) -> int: ...
+ @overload
+ def getall(self, key: _S) -> List[_T]: ...
+ @overload
+ def getall(self, key: _S, default: _D) -> Union[List[_T], _D]: ...
+ @overload
+ def getone(self, key: _S) -> _T: ...
+ @overload
+ def getone(self, key: _S, default: _D) -> Union[_T, _D]: ...
+ def add(self, key: _S, value: _T) -> None: ...
+ def extend(self, arg: _Arg[_T] = ..., **kwargs: _T) -> None: ...
+ @overload
+ def popone(self, key: _S) -> _T: ...
+ @overload
+ def popone(self, key: _S, default: _D) -> Union[_T, _D]: ...
+ @overload
+ def popall(self, key: _S) -> List[_T]: ...
+ @overload
+ def popall(self, key: _S, default: _D) -> Union[List[_T], _D]: ...
+
+class MultiDictProxy(MultiMapping[_T], Generic[_T]):
+ def __init__(
+ self, arg: Union[MultiMapping[_T], MutableMultiMapping[_T]]
+ ) -> None: ...
+ def copy(self) -> MultiDict[_T]: ...
+ def __getitem__(self, k: _S) -> _T: ...
+ def __iter__(self) -> Iterator[_S]: ...
+ def __len__(self) -> int: ...
+ @overload
+ def getall(self, key: _S) -> List[_T]: ...
+ @overload
+ def getall(self, key: _S, default: _D) -> Union[List[_T], _D]: ...
+ @overload
+ def getone(self, key: _S) -> _T: ...
+ @overload
+ def getone(self, key: _S, default: _D) -> Union[_T, _D]: ...
+
+class CIMultiDictProxy(MultiMapping[_T], Generic[_T]):
+ def __init__(
+ self, arg: Union[MultiMapping[_T], MutableMultiMapping[_T]]
+ ) -> None: ...
+ def __getitem__(self, k: _S) -> _T: ...
+ def __iter__(self) -> Iterator[_S]: ...
+ def __len__(self) -> int: ...
+ @overload
+ def getall(self, key: _S) -> List[_T]: ...
+ @overload
+ def getall(self, key: _S, default: _D) -> Union[List[_T], _D]: ...
+ @overload
+ def getone(self, key: _S) -> _T: ...
+ @overload
+ def getone(self, key: _S, default: _D) -> Union[_T, _D]: ...
+ def copy(self) -> CIMultiDict[_T]: ...
+
+def getversion(
+ md: Union[MultiDict[_T], CIMultiDict[_T], MultiDictProxy[_T], CIMultiDictProxy[_T]]
+) -> int: ...
diff --git a/third_party/python/multidict/multidict/_abc.py b/third_party/python/multidict/multidict/_abc.py
new file mode 100644
index 0000000000..0603cdd244
--- /dev/null
+++ b/third_party/python/multidict/multidict/_abc.py
@@ -0,0 +1,48 @@
+import abc
+import sys
+import types
+from collections.abc import Mapping, MutableMapping
+
+
+class _TypingMeta(abc.ABCMeta):
+ # A fake metaclass to satisfy typing deps in runtime
+ # basically MultiMapping[str] and other generic-like type instantiations
+ # are emulated.
+ # Note: real type hints are provided by __init__.pyi stub file
+ if sys.version_info >= (3, 9):
+
+ def __getitem__(self, key):
+ return types.GenericAlias(self, key)
+
+ else:
+
+ def __getitem__(self, key):
+ return self
+
+
+class MultiMapping(Mapping, metaclass=_TypingMeta):
+ @abc.abstractmethod
+ def getall(self, key, default=None):
+ raise KeyError
+
+ @abc.abstractmethod
+ def getone(self, key, default=None):
+ raise KeyError
+
+
+class MutableMultiMapping(MultiMapping, MutableMapping):
+ @abc.abstractmethod
+ def add(self, key, value):
+ raise NotImplementedError
+
+ @abc.abstractmethod
+ def extend(self, *args, **kwargs):
+ raise NotImplementedError
+
+ @abc.abstractmethod
+ def popone(self, key, default=None):
+ raise KeyError
+
+ @abc.abstractmethod
+ def popall(self, key, default=None):
+ raise KeyError
diff --git a/third_party/python/multidict/multidict/_compat.py b/third_party/python/multidict/multidict/_compat.py
new file mode 100644
index 0000000000..e659124558
--- /dev/null
+++ b/third_party/python/multidict/multidict/_compat.py
@@ -0,0 +1,14 @@
+import os
+import platform
+
+NO_EXTENSIONS = bool(os.environ.get("MULTIDICT_NO_EXTENSIONS"))
+
+PYPY = platform.python_implementation() == "PyPy"
+
+USE_CYTHON_EXTENSIONS = USE_CYTHON = not NO_EXTENSIONS and not PYPY
+
+if USE_CYTHON_EXTENSIONS:
+ try:
+ from . import _multidict # noqa
+ except ImportError:
+ USE_CYTHON_EXTENSIONS = USE_CYTHON = False
diff --git a/third_party/python/multidict/multidict/_multidict.c b/third_party/python/multidict/multidict/_multidict.c
new file mode 100644
index 0000000000..5bdcc898de
--- /dev/null
+++ b/third_party/python/multidict/multidict/_multidict.c
@@ -0,0 +1,1646 @@
+#include "Python.h"
+#include "structmember.h"
+
+// Include order important
+#include "_multilib/defs.h"
+#include "_multilib/istr.h"
+#include "_multilib/pair_list.h"
+#include "_multilib/dict.h"
+#include "_multilib/iter.h"
+#include "_multilib/views.h"
+
+static PyObject *collections_abc_mapping;
+static PyObject *collections_abc_mut_mapping;
+static PyObject *collections_abc_mut_multi_mapping;
+
+static PyTypeObject multidict_type;
+static PyTypeObject cimultidict_type;
+static PyTypeObject multidict_proxy_type;
+static PyTypeObject cimultidict_proxy_type;
+
+static PyObject *repr_func;
+
+#define MultiDict_CheckExact(o) (Py_TYPE(o) == &multidict_type)
+#define CIMultiDict_CheckExact(o) (Py_TYPE(o) == &cimultidict_type)
+#define MultiDictProxy_CheckExact(o) (Py_TYPE(o) == &multidict_proxy_type)
+#define CIMultiDictProxy_CheckExact(o) (Py_TYPE(o) == &cimultidict_proxy_type)
+
+/* Helper macro for something like isinstance(obj, Base) */
+#define _MultiDict_Check(o) \
+ ((MultiDict_CheckExact(o)) || \
+ (CIMultiDict_CheckExact(o)) || \
+ (MultiDictProxy_CheckExact(o)) || \
+ (CIMultiDictProxy_CheckExact(o)))
+
+/******************** Internal Methods ********************/
+
+/* Forward declaration */
+static PyObject *multidict_items(MultiDictObject *self);
+
+static inline PyObject *
+_multidict_getone(MultiDictObject *self, PyObject *key, PyObject *_default)
+{
+ PyObject *val = pair_list_get_one(&self->pairs, key);
+
+ if (val == NULL &&
+ PyErr_ExceptionMatches(PyExc_KeyError) &&
+ _default != NULL)
+ {
+ PyErr_Clear();
+ Py_INCREF(_default);
+ return _default;
+ }
+
+ return val;
+}
+
+static inline int
+_multidict_eq(MultiDictObject *self, MultiDictObject *other)
+{
+ Py_ssize_t pos1 = 0,
+ pos2 = 0;
+
+ Py_hash_t h1 = 0,
+ h2 = 0;
+
+ PyObject *identity1 = NULL,
+ *identity2 = NULL,
+ *value1 = NULL,
+ *value2 = NULL;
+
+ int cmp_identity = 0,
+ cmp_value = 0;
+
+ if (self == other) {
+ return 1;
+ }
+
+ if (pair_list_len(&self->pairs) != pair_list_len(&other->pairs)) {
+ return 0;
+ }
+
+ while (_pair_list_next(&self->pairs, &pos1, &identity1, NULL, &value1, &h1) &&
+ _pair_list_next(&other->pairs, &pos2, &identity2, NULL, &value2, &h2))
+ {
+ if (h1 != h2) {
+ return 0;
+ }
+ cmp_identity = PyObject_RichCompareBool(identity1, identity2, Py_NE);
+ if (cmp_identity < 0) {
+ return -1;
+ }
+ cmp_value = PyObject_RichCompareBool(value1, value2, Py_NE);
+ if (cmp_value < 0) {
+ return -1;
+ }
+ if (cmp_identity || cmp_value) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static inline int
+_multidict_update_items(MultiDictObject *self, pair_list_t *pairs)
+{
+ return pair_list_update(&self->pairs, pairs);
+}
+
+static inline int
+_multidict_append_items(MultiDictObject *self, pair_list_t *pairs)
+{
+ PyObject *key = NULL,
+ *value = NULL;
+
+ Py_ssize_t pos = 0;
+
+ while (_pair_list_next(pairs, &pos, NULL, &key, &value, NULL)) {
+ if (pair_list_add(&self->pairs, key, value) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static inline int
+_multidict_append_items_seq(MultiDictObject *self, PyObject *arg,
+ const char *name)
+{
+ PyObject *key = NULL,
+ *value = NULL,
+ *item = NULL,
+ *iter = PyObject_GetIter(arg);
+
+ if (iter == NULL) {
+ return -1;
+ }
+
+ while ((item = PyIter_Next(iter)) != NULL) {
+ if (PyTuple_CheckExact(item)) {
+ if (PyTuple_GET_SIZE(item) != 2) {
+ goto invalid_type;
+ }
+ key = PyTuple_GET_ITEM(item, 0);
+ Py_INCREF(key);
+ value = PyTuple_GET_ITEM(item, 1);
+ Py_INCREF(value);
+ }
+ else if (PyList_CheckExact(item)) {
+ if (PyList_GET_SIZE(item) != 2) {
+ goto invalid_type;
+ }
+ key = PyList_GET_ITEM(item, 0);
+ Py_INCREF(key);
+ value = PyList_GET_ITEM(item, 1);
+ Py_INCREF(value);
+ }
+ else if (PySequence_Check(item)) {
+ if (PySequence_Size(item) != 2) {
+ goto invalid_type;
+ }
+ key = PySequence_GetItem(item, 0);
+ value = PySequence_GetItem(item, 1);
+ } else {
+ goto invalid_type;
+ }
+
+ if (pair_list_add(&self->pairs, key, value) < 0) {
+ goto fail;
+ }
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ Py_CLEAR(item);
+ }
+
+ Py_DECREF(iter);
+
+ if (PyErr_Occurred()) {
+ return -1;
+ }
+
+ return 0;
+invalid_type:
+ PyErr_Format(
+ PyExc_TypeError,
+ "%s takes either dict or list of (key, value) pairs",
+ name,
+ NULL
+ );
+ goto fail;
+fail:
+ Py_XDECREF(key);
+ Py_XDECREF(value);
+ Py_XDECREF(item);
+ Py_DECREF(iter);
+ return -1;
+}
+
+static inline int
+_multidict_list_extend(PyObject *list, PyObject *target_list)
+{
+ PyObject *item = NULL,
+ *iter = PyObject_GetIter(target_list);
+
+ if (iter == NULL) {
+ return -1;
+ }
+
+ while ((item = PyIter_Next(iter)) != NULL) {
+ if (PyList_Append(list, item) < 0) {
+ Py_DECREF(item);
+ Py_DECREF(iter);
+ return -1;
+ }
+ Py_DECREF(item);
+ }
+
+ Py_DECREF(iter);
+
+ if (PyErr_Occurred()) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline int
+_multidict_extend_with_args(MultiDictObject *self, PyObject *arg,
+ PyObject *kwds, const char *name, int do_add)
+{
+ PyObject *arg_items = NULL, /* tracked by GC */
+ *kwds_items = NULL; /* new reference */
+ pair_list_t *pairs = NULL;
+
+ int err = 0;
+
+ if (kwds && !PyArg_ValidateKeywordArguments(kwds)) {
+ return -1;
+ }
+
+ // TODO: mb can be refactored more clear
+ if (_MultiDict_Check(arg) && kwds == NULL) {
+ if (MultiDict_CheckExact(arg) || CIMultiDict_CheckExact(arg)) {
+ pairs = &((MultiDictObject*)arg)->pairs;
+ } else if (MultiDictProxy_CheckExact(arg) || CIMultiDictProxy_CheckExact(arg)) {
+ pairs = &((MultiDictProxyObject*)arg)->md->pairs;
+ }
+
+ if (do_add) {
+ return _multidict_append_items(self, pairs);
+ }
+
+ return _multidict_update_items(self, pairs);
+ }
+
+ if (PyObject_HasAttrString(arg, "items")) {
+ if (_MultiDict_Check(arg)) {
+ arg_items = multidict_items((MultiDictObject*)arg);
+ } else {
+ arg_items = PyMapping_Items(arg);
+ }
+ if (arg_items == NULL) {
+ return -1;
+ }
+ } else {
+ arg_items = arg;
+ Py_INCREF(arg_items);
+ }
+
+ if (kwds) {
+ PyObject *tmp = PySequence_List(arg_items);
+ Py_DECREF(arg_items);
+ arg_items = tmp;
+ if (arg_items == NULL) {
+ return -1;
+ }
+
+ kwds_items = PyDict_Items(kwds);
+ if (kwds_items == NULL) {
+ Py_DECREF(arg_items);
+ return -1;
+ }
+ err = _multidict_list_extend(arg_items, kwds_items);
+ Py_DECREF(kwds_items);
+ if (err < 0) {
+ Py_DECREF(arg_items);
+ return -1;
+ }
+ }
+
+ if (do_add) {
+ err = _multidict_append_items_seq(self, arg_items, name);
+ } else {
+ err = pair_list_update_from_seq(&self->pairs, arg_items);
+ }
+
+ Py_DECREF(arg_items);
+
+ return err;
+}
+
+static inline int
+_multidict_extend_with_kwds(MultiDictObject *self, PyObject *kwds,
+ const char *name, int do_add)
+{
+ PyObject *arg = NULL;
+
+ int err = 0;
+
+ if (!PyArg_ValidateKeywordArguments(kwds)) {
+ return -1;
+ }
+
+ arg = PyDict_Items(kwds);
+ if (do_add) {
+ err = _multidict_append_items_seq(self, arg, name);
+ } else {
+ err = pair_list_update_from_seq(&self->pairs, arg);
+ }
+
+ Py_DECREF(arg);
+ return err;
+}
+
+static inline int
+_multidict_extend(MultiDictObject *self, PyObject *args, PyObject *kwds,
+ const char *name, int do_add)
+{
+ PyObject *arg = NULL;
+
+ if (args && PyObject_Length(args) > 1) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "%s takes at most 1 positional argument (%zd given)",
+ name, PyObject_Length(args), NULL
+ );
+ return -1;
+ }
+
+ if (args && PyObject_Length(args) > 0) {
+ if (!PyArg_UnpackTuple(args, name, 0, 1, &arg)) {
+ return -1;
+ }
+ if (_multidict_extend_with_args(self, arg, kwds, name, do_add) < 0) {
+ return -1;
+ }
+ } else if (kwds && PyObject_Length(kwds) > 0) {
+ if (_multidict_extend_with_kwds(self, kwds, name, do_add) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static inline PyObject *
+_multidict_copy(MultiDictObject *self, PyTypeObject *multidict_tp_object)
+{
+ MultiDictObject *new_multidict = NULL;
+
+ PyObject *arg_items = NULL,
+ *items = NULL;
+
+ new_multidict = (MultiDictObject*)PyType_GenericNew(
+ multidict_tp_object, NULL, NULL);
+ if (new_multidict == NULL) {
+ return NULL;
+ }
+
+ if (multidict_tp_object->tp_init(
+ (PyObject*)new_multidict, NULL, NULL) < 0)
+ {
+ return NULL;
+ }
+
+ items = multidict_items(self);
+ if (items == NULL) {
+ goto fail;
+ }
+
+ // TODO: "Implementation looks as slow as possible ..."
+ arg_items = PyTuple_New(1);
+ if (arg_items == NULL) {
+ goto fail;
+ }
+
+ Py_INCREF(items);
+ PyTuple_SET_ITEM(arg_items, 0, items);
+
+ if (_multidict_extend(
+ new_multidict, arg_items, NULL, "copy", 1) < 0)
+ {
+ goto fail;
+ }
+
+ Py_DECREF(items);
+ Py_DECREF(arg_items);
+
+ return (PyObject*)new_multidict;
+
+fail:
+ Py_XDECREF(items);
+ Py_XDECREF(arg_items);
+
+ Py_DECREF(new_multidict);
+
+ return NULL;
+}
+
+static inline PyObject *
+_multidict_proxy_copy(MultiDictProxyObject *self, PyTypeObject *type)
+{
+ PyObject *new_multidict = PyType_GenericNew(type, NULL, NULL);
+ if (new_multidict == NULL) {
+ goto fail;
+ }
+ if (type->tp_init(new_multidict, NULL, NULL) < 0) {
+ goto fail;
+ }
+ if (_multidict_extend_with_args(
+ (MultiDictObject*)new_multidict, (PyObject*)self, NULL, "copy", 1) < 0)
+ {
+ goto fail;
+ }
+
+ return new_multidict;
+
+fail:
+ Py_XDECREF(new_multidict);
+ return NULL;
+}
+
+
+/******************** Base Methods ********************/
+
+static inline PyObject *
+multidict_getall(MultiDictObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *list = NULL,
+ *key = NULL,
+ *_default = NULL;
+
+ static char *getall_keywords[] = {"key", "default", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:getall",
+ getall_keywords, &key, &_default))
+ {
+ return NULL;
+ }
+
+ list = pair_list_get_all(&self->pairs, key);
+
+ if (list == NULL &&
+ PyErr_ExceptionMatches(PyExc_KeyError) &&
+ _default != NULL)
+ {
+ PyErr_Clear();
+ Py_INCREF(_default);
+ return _default;
+ }
+
+ return list;
+}
+
+static inline PyObject *
+multidict_getone(MultiDictObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *key = NULL,
+ *_default = NULL;
+
+ static char *getone_keywords[] = {"key", "default", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:getone",
+ getone_keywords, &key, &_default))
+ {
+ return NULL;
+ }
+
+ return _multidict_getone(self, key, _default);
+}
+
+static inline PyObject *
+multidict_get(MultiDictObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *key = NULL,
+ *_default = Py_None,
+ *ret;
+
+ static char *getone_keywords[] = {"key", "default", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:getone",
+ getone_keywords, &key, &_default))
+ {
+ return NULL;
+ }
+ ret = _multidict_getone(self, key, _default);
+ return ret;
+}
+
+static inline PyObject *
+multidict_keys(MultiDictObject *self)
+{
+ return multidict_keysview_new((PyObject*)self);
+}
+
+static inline PyObject *
+multidict_items(MultiDictObject *self)
+{
+ return multidict_itemsview_new((PyObject*)self);
+}
+
+static inline PyObject *
+multidict_values(MultiDictObject *self)
+{
+ return multidict_valuesview_new((PyObject*)self);
+}
+
+static inline PyObject *
+multidict_reduce(MultiDictObject *self)
+{
+ PyObject *items = NULL,
+ *items_list = NULL,
+ *args = NULL,
+ *result = NULL;
+
+ items = multidict_items(self);
+ if (items == NULL) {
+ goto ret;
+ }
+
+ items_list = PySequence_List(items);
+ if (items_list == NULL) {
+ goto ret;
+ }
+
+ args = PyTuple_Pack(1, items_list);
+ if (args == NULL) {
+ goto ret;
+ }
+
+ result = PyTuple_Pack(2, Py_TYPE(self), args);
+
+ret:
+ Py_XDECREF(args);
+ Py_XDECREF(items_list);
+ Py_XDECREF(items);
+
+ return result;
+}
+
+static inline PyObject *
+multidict_repr(PyObject *self)
+{
+ return PyObject_CallFunctionObjArgs(
+ repr_func, self, NULL);
+}
+
+static inline Py_ssize_t
+multidict_mp_len(MultiDictObject *self)
+{
+ return pair_list_len(&self->pairs);
+}
+
+static inline PyObject *
+multidict_mp_subscript(MultiDictObject *self, PyObject *key)
+{
+ return _multidict_getone(self, key, NULL);
+}
+
+static inline int
+multidict_mp_as_subscript(MultiDictObject *self, PyObject *key, PyObject *val)
+{
+ if (val == NULL) {
+ return pair_list_del(&self->pairs, key);
+ } else {
+ return pair_list_replace(&self->pairs, key, val);
+ }
+}
+
+static inline int
+multidict_sq_contains(MultiDictObject *self, PyObject *key)
+{
+ return pair_list_contains(&self->pairs, key);
+}
+
+static inline PyObject *
+multidict_tp_iter(MultiDictObject *self)
+{
+ return multidict_keys_iter_new(self);
+}
+
+static inline PyObject *
+multidict_tp_richcompare(PyObject *self, PyObject *other, int op)
+{
+ // TODO: refactoring me with love
+
+ int cmp = 0;
+
+ if (op != Py_EQ && op != Py_NE) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+
+ if (MultiDict_CheckExact(other) || CIMultiDict_CheckExact(other)) {
+ cmp = _multidict_eq(
+ (MultiDictObject*)self,
+ (MultiDictObject*)other
+ );
+ if (cmp < 0) {
+ return NULL;
+ }
+ if (op == Py_NE) {
+ cmp = !cmp;
+ }
+ return PyBool_FromLong(cmp);
+ }
+
+ if (MultiDictProxy_CheckExact(other) || CIMultiDictProxy_CheckExact(other)) {
+ cmp = _multidict_eq(
+ (MultiDictObject*)self,
+ ((MultiDictProxyObject*)other)->md
+ );
+ if (cmp < 0) {
+ return NULL;
+ }
+ if (op == Py_NE) {
+ cmp = !cmp;
+ }
+ return PyBool_FromLong(cmp);
+ }
+
+ cmp = PyObject_IsInstance(other, (PyObject*)collections_abc_mapping);
+ if (cmp < 0) {
+ return NULL;
+ }
+
+ if (cmp) {
+ cmp = pair_list_eq_to_mapping(&((MultiDictObject*)self)->pairs, other);
+ if (cmp < 0) {
+ return NULL;
+ }
+ if (op == Py_NE) {
+ cmp = !cmp;
+ }
+ return PyBool_FromLong(cmp);
+ }
+
+ Py_RETURN_NOTIMPLEMENTED;
+}
+
+static inline void
+multidict_tp_dealloc(MultiDictObject *self)
+{
+ PyObject_GC_UnTrack(self);
+ Py_TRASHCAN_SAFE_BEGIN(self);
+ if (self->weaklist != NULL) {
+ PyObject_ClearWeakRefs((PyObject *)self);
+ };
+ pair_list_dealloc(&self->pairs);
+ Py_TYPE(self)->tp_free((PyObject *)self);
+ Py_TRASHCAN_SAFE_END(self);
+}
+
+static inline int
+multidict_tp_traverse(MultiDictObject *self, visitproc visit, void *arg)
+{
+ return pair_list_traverse(&self->pairs, visit, arg);
+}
+
+static inline int
+multidict_tp_clear(MultiDictObject *self)
+{
+ return pair_list_clear(&self->pairs);
+}
+
+PyDoc_STRVAR(multidict_getall_doc,
+"Return a list of all values matching the key.");
+
+PyDoc_STRVAR(multidict_getone_doc,
+"Get first value matching the key.");
+
+PyDoc_STRVAR(multidict_get_doc,
+"Get first value matching the key.\n\nThe method is alias for .getone().");
+
+PyDoc_STRVAR(multidict_keys_doc,
+"Return a new view of the dictionary's keys.");
+
+PyDoc_STRVAR(multidict_items_doc,
+"Return a new view of the dictionary's items *(key, value) pairs).");
+
+PyDoc_STRVAR(multidict_values_doc,
+"Return a new view of the dictionary's values.");
+
+/******************** MultiDict ********************/
+
+static inline int
+multidict_tp_init(MultiDictObject *self, PyObject *args, PyObject *kwds)
+{
+ if (pair_list_init(&self->pairs) < 0) {
+ return -1;
+ }
+ if (_multidict_extend(self, args, kwds, "MultiDict", 1) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static inline PyObject *
+multidict_add(MultiDictObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *key = NULL,
+ *val = NULL;
+
+ static char *kwlist[] = {"key", "value", NULL};
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO:add",
+ kwlist, &key, &val))
+ {
+ return NULL;
+ }
+
+ if (pair_list_add(&self->pairs, key, val) < 0) {
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static inline PyObject *
+multidict_copy(MultiDictObject *self)
+{
+ return _multidict_copy(self, &multidict_type);
+}
+
+static inline PyObject *
+multidict_extend(MultiDictObject *self, PyObject *args, PyObject *kwds)
+{
+ if (_multidict_extend(self, args, kwds, "extend", 1) < 0) {
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static inline PyObject *
+multidict_clear(MultiDictObject *self)
+{
+ if (pair_list_clear(&self->pairs) < 0) {
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static inline PyObject *
+multidict_setdefault(MultiDictObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *key = NULL,
+ *_default = NULL;
+
+ static char *setdefault_keywords[] = {"key", "default", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:setdefault",
+ setdefault_keywords, &key, &_default))
+ {
+ return NULL;
+ }
+ return pair_list_set_default(&self->pairs, key, _default);
+}
+
+static inline PyObject *
+multidict_popone(MultiDictObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *key = NULL,
+ *_default = NULL,
+ *ret_val = NULL;
+
+ static char *popone_keywords[] = {"key", "default", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:popone",
+ popone_keywords, &key, &_default))
+ {
+ return NULL;
+ }
+
+ ret_val = pair_list_pop_one(&self->pairs, key);
+
+ if (ret_val == NULL &&
+ PyErr_ExceptionMatches(PyExc_KeyError) &&
+ _default != NULL)
+ {
+ PyErr_Clear();
+ Py_INCREF(_default);
+ return _default;
+ }
+
+ return ret_val;
+}
+
+static inline PyObject *
+multidict_popall(MultiDictObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *key = NULL,
+ *_default = NULL,
+ *ret_val = NULL;
+
+ static char *popall_keywords[] = {"key", "default", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:popall",
+ popall_keywords, &key, &_default))
+ {
+ return NULL;
+ }
+
+ ret_val = pair_list_pop_all(&self->pairs, key);
+
+ if (ret_val == NULL &&
+ PyErr_ExceptionMatches(PyExc_KeyError) &&
+ _default != NULL)
+ {
+ PyErr_Clear();
+ Py_INCREF(_default);
+ return _default;
+ }
+
+ return ret_val;
+}
+
+static inline PyObject *
+multidict_popitem(MultiDictObject *self)
+{
+ return pair_list_pop_item(&self->pairs);
+}
+
+static inline PyObject *
+multidict_update(MultiDictObject *self, PyObject *args, PyObject *kwds)
+{
+ if (_multidict_extend(self, args, kwds, "update", 0) < 0) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(multidict_add_doc,
+"Add the key and value, not overwriting any previous value.");
+
+PyDoc_STRVAR(multidict_copy_doc,
+"Return a copy of itself.");
+
+PyDoc_STRVAR(multdicit_method_extend_doc,
+"Extend current MultiDict with more values.\n\
+This method must be used instead of update.");
+
+PyDoc_STRVAR(multidict_clear_doc,
+"Remove all items from MultiDict");
+
+PyDoc_STRVAR(multidict_setdefault_doc,
+"Return value for key, set value to default if key is not present.");
+
+PyDoc_STRVAR(multidict_popone_doc,
+"Remove the last occurrence of key and return the corresponding value.\n\n\
+If key is not found, default is returned if given, otherwise KeyError is \
+raised.\n");
+
+PyDoc_STRVAR(multidict_popall_doc,
+"Remove all occurrences of key and return the list of corresponding values.\n\n\
+If key is not found, default is returned if given, otherwise KeyError is \
+raised.\n");
+
+PyDoc_STRVAR(multidict_popitem_doc,
+"Remove and return an arbitrary (key, value) pair.");
+
+PyDoc_STRVAR(multidict_update_doc,
+"Update the dictionary from *other*, overwriting existing keys.");
+
+
+#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 9
+#define multidict_class_getitem Py_GenericAlias
+#else
+static inline PyObject *
+multidict_class_getitem(PyObject *self, PyObject *arg)
+{
+ Py_INCREF(self);
+ return self;
+}
+#endif
+
+
+PyDoc_STRVAR(sizeof__doc__,
+"D.__sizeof__() -> size of D in memory, in bytes");
+
+static inline PyObject *
+_multidict_sizeof(MultiDictObject *self)
+{
+ Py_ssize_t size = sizeof(MultiDictObject);
+ if (self->pairs.pairs != self->pairs.buffer) {
+ size += (Py_ssize_t)sizeof(pair_t) * self->pairs.capacity;
+ }
+ return PyLong_FromSsize_t(size);
+}
+
+
+static PySequenceMethods multidict_sequence = {
+ .sq_contains = (objobjproc)multidict_sq_contains,
+};
+
+static PyMappingMethods multidict_mapping = {
+ .mp_length = (lenfunc)multidict_mp_len,
+ .mp_subscript = (binaryfunc)multidict_mp_subscript,
+ .mp_ass_subscript = (objobjargproc)multidict_mp_as_subscript,
+};
+
+static PyMethodDef multidict_methods[] = {
+ {
+ "getall",
+ (PyCFunction)multidict_getall,
+ METH_VARARGS | METH_KEYWORDS,
+ multidict_getall_doc
+ },
+ {
+ "getone",
+ (PyCFunction)multidict_getone,
+ METH_VARARGS | METH_KEYWORDS,
+ multidict_getone_doc
+ },
+ {
+ "get",
+ (PyCFunction)multidict_get,
+ METH_VARARGS | METH_KEYWORDS,
+ multidict_get_doc
+ },
+ {
+ "keys",
+ (PyCFunction)multidict_keys,
+ METH_NOARGS,
+ multidict_keys_doc
+ },
+ {
+ "items",
+ (PyCFunction)multidict_items,
+ METH_NOARGS,
+ multidict_items_doc
+ },
+ {
+ "values",
+ (PyCFunction)multidict_values,
+ METH_NOARGS,
+ multidict_values_doc
+ },
+ {
+ "add",
+ (PyCFunction)multidict_add,
+ METH_VARARGS | METH_KEYWORDS,
+ multidict_add_doc
+ },
+ {
+ "copy",
+ (PyCFunction)multidict_copy,
+ METH_NOARGS,
+ multidict_copy_doc
+ },
+ {
+ "extend",
+ (PyCFunction)multidict_extend,
+ METH_VARARGS | METH_KEYWORDS,
+ multdicit_method_extend_doc
+ },
+ {
+ "clear",
+ (PyCFunction)multidict_clear,
+ METH_NOARGS,
+ multidict_clear_doc
+ },
+ {
+ "setdefault",
+ (PyCFunction)multidict_setdefault,
+ METH_VARARGS | METH_KEYWORDS,
+ multidict_setdefault_doc
+ },
+ {
+ "popone",
+ (PyCFunction)multidict_popone,
+ METH_VARARGS | METH_KEYWORDS,
+ multidict_popone_doc
+ },
+ {
+ "pop",
+ (PyCFunction)multidict_popone,
+ METH_VARARGS | METH_KEYWORDS,
+ multidict_popone_doc
+ },
+ {
+ "popall",
+ (PyCFunction)multidict_popall,
+ METH_VARARGS | METH_KEYWORDS,
+ multidict_popall_doc
+ },
+ {
+ "popitem",
+ (PyCFunction)multidict_popitem,
+ METH_NOARGS,
+ multidict_popitem_doc
+ },
+ {
+ "update",
+ (PyCFunction)multidict_update,
+ METH_VARARGS | METH_KEYWORDS,
+ multidict_update_doc
+ },
+ {
+ "__reduce__",
+ (PyCFunction)multidict_reduce,
+ METH_NOARGS,
+ NULL,
+ },
+ {
+ "__class_getitem__",
+ (PyCFunction)multidict_class_getitem,
+ METH_O | METH_CLASS,
+ NULL
+ },
+ {
+ "__sizeof__",
+ (PyCFunction)_multidict_sizeof,
+ METH_NOARGS,
+ sizeof__doc__,
+ },
+ {
+ NULL,
+ NULL
+ } /* sentinel */
+};
+
+
+PyDoc_STRVAR(MultDict_doc,
+"Dictionary with the support for duplicate keys.");
+
+
+static PyTypeObject multidict_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "multidict._multidict.MultiDict", /* tp_name */
+ sizeof(MultiDictObject), /* tp_basicsize */
+ .tp_dealloc = (destructor)multidict_tp_dealloc,
+ .tp_repr = (reprfunc)multidict_repr,
+ .tp_as_sequence = &multidict_sequence,
+ .tp_as_mapping = &multidict_mapping,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
+ .tp_doc = MultDict_doc,
+ .tp_traverse = (traverseproc)multidict_tp_traverse,
+ .tp_clear = (inquiry)multidict_tp_clear,
+ .tp_richcompare = (richcmpfunc)multidict_tp_richcompare,
+ .tp_weaklistoffset = offsetof(MultiDictObject, weaklist),
+ .tp_iter = (getiterfunc)multidict_tp_iter,
+ .tp_methods = multidict_methods,
+ .tp_init = (initproc)multidict_tp_init,
+ .tp_alloc = PyType_GenericAlloc,
+ .tp_new = PyType_GenericNew,
+ .tp_free = PyObject_GC_Del,
+};
+
+/******************** CIMultiDict ********************/
+
+static inline int
+cimultidict_tp_init(MultiDictObject *self, PyObject *args, PyObject *kwds)
+{
+ if (ci_pair_list_init(&self->pairs) < 0) {
+ return -1;
+ }
+ if (_multidict_extend(self, args, kwds, "CIMultiDict", 1) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static inline PyObject *
+cimultidict_copy(MultiDictObject *self)
+{
+ return _multidict_copy(self, &cimultidict_type);
+}
+
+PyDoc_STRVAR(cimultidict_copy_doc,
+"Return a copy of itself.");
+
+static PyMethodDef cimultidict_methods[] = {
+ {
+ "copy",
+ (PyCFunction)cimultidict_copy,
+ METH_NOARGS,
+ cimultidict_copy_doc
+ },
+ {
+ NULL,
+ NULL
+ } /* sentinel */
+};
+
+PyDoc_STRVAR(CIMultDict_doc,
+"Dictionary with the support for duplicate case-insensitive keys.");
+
+
+static PyTypeObject cimultidict_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "multidict._multidict.CIMultiDict", /* tp_name */
+ sizeof(MultiDictObject), /* tp_basicsize */
+ .tp_dealloc = (destructor)multidict_tp_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
+ .tp_doc = CIMultDict_doc,
+ .tp_traverse = (traverseproc)multidict_tp_traverse,
+ .tp_clear = (inquiry)multidict_tp_clear,
+ .tp_weaklistoffset = offsetof(MultiDictObject, weaklist),
+ .tp_methods = cimultidict_methods,
+ .tp_base = &multidict_type,
+ .tp_init = (initproc)cimultidict_tp_init,
+ .tp_alloc = PyType_GenericAlloc,
+ .tp_new = PyType_GenericNew,
+ .tp_free = PyObject_GC_Del,
+};
+
+/******************** MultiDictProxy ********************/
+
+static inline int
+multidict_proxy_tp_init(MultiDictProxyObject *self, PyObject *args,
+ PyObject *kwds)
+{
+ PyObject *arg = NULL;
+ MultiDictObject *md = NULL;
+
+ if (!PyArg_UnpackTuple(args, "multidict._multidict.MultiDictProxy",
+ 0, 1, &arg))
+ {
+ return -1;
+ }
+ if (arg == NULL) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "__init__() missing 1 required positional argument: 'arg'"
+ );
+ return -1;
+ }
+ if (!MultiDictProxy_CheckExact(arg) &&
+ !CIMultiDict_CheckExact(arg) &&
+ !MultiDict_CheckExact(arg))
+ {
+ PyErr_Format(
+ PyExc_TypeError,
+ "ctor requires MultiDict or MultiDictProxy instance, "
+ "not <classs '%s'>",
+ Py_TYPE(arg)->tp_name
+ );
+ return -1;
+ }
+
+ md = (MultiDictObject*)arg;
+ if (MultiDictProxy_CheckExact(arg)) {
+ md = ((MultiDictProxyObject*)arg)->md;
+ }
+ Py_INCREF(md);
+ self->md = md;
+
+ return 0;
+}
+
+static inline PyObject *
+multidict_proxy_getall(MultiDictProxyObject *self, PyObject *args,
+ PyObject *kwds)
+{
+ return multidict_getall(self->md, args, kwds);
+}
+
+static inline PyObject *
+multidict_proxy_getone(MultiDictProxyObject *self, PyObject *args,
+ PyObject *kwds)
+{
+ return multidict_getone(self->md, args, kwds);
+}
+
+static inline PyObject *
+multidict_proxy_get(MultiDictProxyObject *self, PyObject *args,
+ PyObject *kwds)
+{
+ return multidict_get(self->md, args, kwds);
+}
+
+static inline PyObject *
+multidict_proxy_keys(MultiDictProxyObject *self)
+{
+ return multidict_keys(self->md);
+}
+
+static inline PyObject *
+multidict_proxy_items(MultiDictProxyObject *self)
+{
+ return multidict_items(self->md);
+}
+
+static inline PyObject *
+multidict_proxy_values(MultiDictProxyObject *self)
+{
+ return multidict_values(self->md);
+}
+
+static inline PyObject *
+multidict_proxy_copy(MultiDictProxyObject *self)
+{
+ return _multidict_proxy_copy(self, &multidict_type);
+}
+
+static inline PyObject *
+multidict_proxy_reduce(MultiDictProxyObject *self)
+{
+ PyErr_Format(
+ PyExc_TypeError,
+ "can't pickle %s objects", Py_TYPE(self)->tp_name
+ );
+
+ return NULL;
+}
+
+static inline Py_ssize_t
+multidict_proxy_mp_len(MultiDictProxyObject *self)
+{
+ return multidict_mp_len(self->md);
+}
+
+static inline PyObject *
+multidict_proxy_mp_subscript(MultiDictProxyObject *self, PyObject *key)
+{
+ return multidict_mp_subscript(self->md, key);
+}
+
+static inline int
+multidict_proxy_sq_contains(MultiDictProxyObject *self, PyObject *key)
+{
+ return multidict_sq_contains(self->md, key);
+}
+
+static inline PyObject *
+multidict_proxy_tp_iter(MultiDictProxyObject *self)
+{
+ return multidict_tp_iter(self->md);
+}
+
+static inline PyObject *
+multidict_proxy_tp_richcompare(MultiDictProxyObject *self, PyObject *other,
+ int op)
+{
+ return multidict_tp_richcompare((PyObject*)self->md, other, op);
+}
+
+static inline void
+multidict_proxy_tp_dealloc(MultiDictProxyObject *self)
+{
+ PyObject_GC_UnTrack(self);
+ if (self->weaklist != NULL) {
+ PyObject_ClearWeakRefs((PyObject *)self);
+ };
+ Py_XDECREF(self->md);
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static inline int
+multidict_proxy_tp_traverse(MultiDictProxyObject *self, visitproc visit,
+ void *arg)
+{
+ Py_VISIT(self->md);
+ return 0;
+}
+
+static inline int
+multidict_proxy_tp_clear(MultiDictProxyObject *self)
+{
+ Py_CLEAR(self->md);
+ return 0;
+}
+
+static PySequenceMethods multidict_proxy_sequence = {
+ .sq_contains = (objobjproc)multidict_proxy_sq_contains,
+};
+
+static PyMappingMethods multidict_proxy_mapping = {
+ .mp_length = (lenfunc)multidict_proxy_mp_len,
+ .mp_subscript = (binaryfunc)multidict_proxy_mp_subscript,
+};
+
+static PyMethodDef multidict_proxy_methods[] = {
+ {
+ "getall",
+ (PyCFunction)multidict_proxy_getall,
+ METH_VARARGS | METH_KEYWORDS,
+ multidict_getall_doc
+ },
+ {
+ "getone",
+ (PyCFunction)multidict_proxy_getone,
+ METH_VARARGS | METH_KEYWORDS,
+ multidict_getone_doc
+ },
+ {
+ "get",
+ (PyCFunction)multidict_proxy_get,
+ METH_VARARGS | METH_KEYWORDS,
+ multidict_get_doc
+ },
+ {
+ "keys",
+ (PyCFunction)multidict_proxy_keys,
+ METH_NOARGS,
+ multidict_keys_doc
+ },
+ {
+ "items",
+ (PyCFunction)multidict_proxy_items,
+ METH_NOARGS,
+ multidict_items_doc
+ },
+ {
+ "values",
+ (PyCFunction)multidict_proxy_values,
+ METH_NOARGS,
+ multidict_values_doc
+ },
+ {
+ "copy",
+ (PyCFunction)multidict_proxy_copy,
+ METH_NOARGS,
+ multidict_copy_doc
+ },
+ {
+ "__reduce__",
+ (PyCFunction)multidict_proxy_reduce,
+ METH_NOARGS,
+ NULL
+ },
+ {
+ "__class_getitem__",
+ (PyCFunction)multidict_class_getitem,
+ METH_O | METH_CLASS,
+ NULL
+ },
+ {
+ NULL,
+ NULL
+ } /* sentinel */
+};
+
+
+PyDoc_STRVAR(MultDictProxy_doc,
+"Read-only proxy for MultiDict instance.");
+
+
+static PyTypeObject multidict_proxy_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "multidict._multidict.MultiDictProxy", /* tp_name */
+ sizeof(MultiDictProxyObject), /* tp_basicsize */
+ .tp_dealloc = (destructor)multidict_proxy_tp_dealloc,
+ .tp_repr = (reprfunc)multidict_repr,
+ .tp_as_sequence = &multidict_proxy_sequence,
+ .tp_as_mapping = &multidict_proxy_mapping,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
+ .tp_doc = MultDictProxy_doc,
+ .tp_traverse = (traverseproc)multidict_proxy_tp_traverse,
+ .tp_clear = (inquiry)multidict_proxy_tp_clear,
+ .tp_richcompare = (richcmpfunc)multidict_proxy_tp_richcompare,
+ .tp_weaklistoffset = offsetof(MultiDictProxyObject, weaklist),
+ .tp_iter = (getiterfunc)multidict_proxy_tp_iter,
+ .tp_methods = multidict_proxy_methods,
+ .tp_init = (initproc)multidict_proxy_tp_init,
+ .tp_alloc = PyType_GenericAlloc,
+ .tp_new = PyType_GenericNew,
+ .tp_free = PyObject_GC_Del,
+};
+
+/******************** CIMultiDictProxy ********************/
+
+static inline int
+cimultidict_proxy_tp_init(MultiDictProxyObject *self, PyObject *args,
+ PyObject *kwds)
+{
+ PyObject *arg = NULL;
+ MultiDictObject *md = NULL;
+
+ if (!PyArg_UnpackTuple(args, "multidict._multidict.CIMultiDictProxy",
+ 1, 1, &arg))
+ {
+ return -1;
+ }
+ if (arg == NULL) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "__init__() missing 1 required positional argument: 'arg'"
+ );
+ return -1;
+ }
+ if (!CIMultiDictProxy_CheckExact(arg) && !CIMultiDict_CheckExact(arg)) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "ctor requires CIMultiDict or CIMultiDictProxy instance, "
+ "not <class '%s'>",
+ Py_TYPE(arg)->tp_name
+ );
+ return -1;
+ }
+
+ md = (MultiDictObject*)arg;
+ if (CIMultiDictProxy_CheckExact(arg)) {
+ md = ((MultiDictProxyObject*)arg)->md;
+ }
+ Py_INCREF(md);
+ self->md = md;
+
+ return 0;
+}
+
+static inline PyObject *
+cimultidict_proxy_copy(MultiDictProxyObject *self)
+{
+ return _multidict_proxy_copy(self, &cimultidict_type);
+}
+
+
+PyDoc_STRVAR(CIMultDictProxy_doc,
+"Read-only proxy for CIMultiDict instance.");
+
+PyDoc_STRVAR(cimultidict_proxy_copy_doc,
+"Return copy of itself");
+
+static PyMethodDef cimultidict_proxy_methods[] = {
+ {
+ "copy",
+ (PyCFunction)cimultidict_proxy_copy,
+ METH_NOARGS,
+ cimultidict_proxy_copy_doc
+ },
+ {
+ NULL,
+ NULL
+ } /* sentinel */
+};
+
+static PyTypeObject cimultidict_proxy_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "multidict._multidict.CIMultiDictProxy", /* tp_name */
+ sizeof(MultiDictProxyObject), /* tp_basicsize */
+ .tp_dealloc = (destructor)multidict_proxy_tp_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
+ .tp_doc = CIMultDictProxy_doc,
+ .tp_traverse = (traverseproc)multidict_proxy_tp_traverse,
+ .tp_clear = (inquiry)multidict_proxy_tp_clear,
+ .tp_richcompare = (richcmpfunc)multidict_proxy_tp_richcompare,
+ .tp_weaklistoffset = offsetof(MultiDictProxyObject, weaklist),
+ .tp_methods = cimultidict_proxy_methods,
+ .tp_base = &multidict_proxy_type,
+ .tp_init = (initproc)cimultidict_proxy_tp_init,
+ .tp_alloc = PyType_GenericAlloc,
+ .tp_new = PyType_GenericNew,
+ .tp_free = PyObject_GC_Del,
+};
+
+/******************** Other functions ********************/
+
+static inline PyObject *
+getversion(PyObject *self, PyObject *md)
+{
+ pair_list_t *pairs = NULL;
+ if (MultiDict_CheckExact(md) || CIMultiDict_CheckExact(md)) {
+ pairs = &((MultiDictObject*)md)->pairs;
+ } else if (MultiDictProxy_CheckExact(md) || CIMultiDictProxy_CheckExact(md)) {
+ pairs = &((MultiDictProxyObject*)md)->md->pairs;
+ } else {
+ PyErr_Format(PyExc_TypeError, "unexpected type");
+ return NULL;
+ }
+ return PyLong_FromUnsignedLong(pair_list_version(pairs));
+}
+
+/******************** Module ********************/
+
+static inline void
+module_free(void *m)
+{
+ Py_CLEAR(collections_abc_mapping);
+ Py_CLEAR(collections_abc_mut_mapping);
+ Py_CLEAR(collections_abc_mut_multi_mapping);
+}
+
+static PyMethodDef multidict_module_methods[] = {
+ {
+ "getversion",
+ (PyCFunction)getversion,
+ METH_O
+ },
+ {
+ NULL,
+ NULL
+ } /* sentinel */
+};
+
+static PyModuleDef multidict_module = {
+ PyModuleDef_HEAD_INIT, /* m_base */
+ "_multidict", /* m_name */
+ .m_size = -1,
+ .m_methods = multidict_module_methods,
+ .m_free = (freefunc)module_free,
+};
+
+PyMODINIT_FUNC
+PyInit__multidict()
+{
+ PyObject *module = NULL,
+ *reg_func_call_result = NULL;
+
+#define WITH_MOD(NAME) \
+ Py_CLEAR(module); \
+ module = PyImport_ImportModule(NAME); \
+ if (module == NULL) { \
+ goto fail; \
+ }
+
+#define GET_MOD_ATTR(VAR, NAME) \
+ VAR = PyObject_GetAttrString(module, NAME); \
+ if (VAR == NULL) { \
+ goto fail; \
+ }
+
+ if (multidict_views_init() < 0) {
+ goto fail;
+ }
+
+ if (multidict_iter_init() < 0) {
+ goto fail;
+ }
+
+ if (istr_init() < 0) {
+ goto fail;
+ }
+
+ if (PyType_Ready(&multidict_type) < 0 ||
+ PyType_Ready(&cimultidict_type) < 0 ||
+ PyType_Ready(&multidict_proxy_type) < 0 ||
+ PyType_Ready(&cimultidict_proxy_type) < 0)
+ {
+ goto fail;
+ }
+
+ WITH_MOD("collections.abc");
+ GET_MOD_ATTR(collections_abc_mapping, "Mapping");
+
+ WITH_MOD("multidict._abc");
+ GET_MOD_ATTR(collections_abc_mut_mapping, "MultiMapping");
+
+ WITH_MOD("multidict._abc");
+ GET_MOD_ATTR(collections_abc_mut_multi_mapping, "MutableMultiMapping");
+
+ WITH_MOD("multidict._multidict_base");
+ GET_MOD_ATTR(repr_func, "_mdrepr");
+
+ /* Register in _abc mappings (CI)MultiDict and (CI)MultiDictProxy */
+ reg_func_call_result = PyObject_CallMethod(
+ collections_abc_mut_mapping,
+ "register", "O",
+ (PyObject*)&multidict_proxy_type
+ );
+ if (reg_func_call_result == NULL) {
+ goto fail;
+ }
+ Py_DECREF(reg_func_call_result);
+
+ reg_func_call_result = PyObject_CallMethod(
+ collections_abc_mut_mapping,
+ "register", "O",
+ (PyObject*)&cimultidict_proxy_type
+ );
+ if (reg_func_call_result == NULL) {
+ goto fail;
+ }
+ Py_DECREF(reg_func_call_result);
+
+ reg_func_call_result = PyObject_CallMethod(
+ collections_abc_mut_multi_mapping,
+ "register", "O",
+ (PyObject*)&multidict_type
+ );
+ if (reg_func_call_result == NULL) {
+ goto fail;
+ }
+ Py_DECREF(reg_func_call_result);
+
+ reg_func_call_result = PyObject_CallMethod(
+ collections_abc_mut_multi_mapping,
+ "register", "O",
+ (PyObject*)&cimultidict_type
+ );
+ if (reg_func_call_result == NULL) {
+ goto fail;
+ }
+ Py_DECREF(reg_func_call_result);
+
+ /* Instantiate this module */
+ module = PyModule_Create(&multidict_module);
+
+ Py_INCREF(&istr_type);
+ if (PyModule_AddObject(
+ module, "istr", (PyObject*)&istr_type) < 0)
+ {
+ goto fail;
+ }
+
+ Py_INCREF(&multidict_type);
+ if (PyModule_AddObject(
+ module, "MultiDict", (PyObject*)&multidict_type) < 0)
+ {
+ goto fail;
+ }
+
+ Py_INCREF(&cimultidict_type);
+ if (PyModule_AddObject(
+ module, "CIMultiDict", (PyObject*)&cimultidict_type) < 0)
+ {
+ goto fail;
+ }
+
+ Py_INCREF(&multidict_proxy_type);
+ if (PyModule_AddObject(
+ module, "MultiDictProxy", (PyObject*)&multidict_proxy_type) < 0)
+ {
+ goto fail;
+ }
+
+ Py_INCREF(&cimultidict_proxy_type);
+ if (PyModule_AddObject(
+ module, "CIMultiDictProxy", (PyObject*)&cimultidict_proxy_type) < 0)
+ {
+ goto fail;
+ }
+
+ return module;
+
+fail:
+ Py_XDECREF(collections_abc_mapping);
+ Py_XDECREF(collections_abc_mut_mapping);
+ Py_XDECREF(collections_abc_mut_multi_mapping);
+
+ return NULL;
+
+#undef WITH_MOD
+#undef GET_MOD_ATTR
+}
diff --git a/third_party/python/multidict/multidict/_multidict_base.py b/third_party/python/multidict/multidict/_multidict_base.py
new file mode 100644
index 0000000000..394466548c
--- /dev/null
+++ b/third_party/python/multidict/multidict/_multidict_base.py
@@ -0,0 +1,144 @@
+from collections.abc import ItemsView, Iterable, KeysView, Set, ValuesView
+
+
+def _abc_itemsview_register(view_cls):
+ ItemsView.register(view_cls)
+
+
+def _abc_keysview_register(view_cls):
+ KeysView.register(view_cls)
+
+
+def _abc_valuesview_register(view_cls):
+ ValuesView.register(view_cls)
+
+
+def _viewbaseset_richcmp(view, other, op):
+ if op == 0: # <
+ if not isinstance(other, Set):
+ return NotImplemented
+ return len(view) < len(other) and view <= other
+ elif op == 1: # <=
+ if not isinstance(other, Set):
+ return NotImplemented
+ if len(view) > len(other):
+ return False
+ for elem in view:
+ if elem not in other:
+ return False
+ return True
+ elif op == 2: # ==
+ if not isinstance(other, Set):
+ return NotImplemented
+ return len(view) == len(other) and view <= other
+ elif op == 3: # !=
+ return not view == other
+ elif op == 4: # >
+ if not isinstance(other, Set):
+ return NotImplemented
+ return len(view) > len(other) and view >= other
+ elif op == 5: # >=
+ if not isinstance(other, Set):
+ return NotImplemented
+ if len(view) < len(other):
+ return False
+ for elem in other:
+ if elem not in view:
+ return False
+ return True
+
+
+def _viewbaseset_and(view, other):
+ if not isinstance(other, Iterable):
+ return NotImplemented
+ if isinstance(view, Set):
+ view = set(iter(view))
+ if isinstance(other, Set):
+ other = set(iter(other))
+ if not isinstance(other, Set):
+ other = set(iter(other))
+ return view & other
+
+
+def _viewbaseset_or(view, other):
+ if not isinstance(other, Iterable):
+ return NotImplemented
+ if isinstance(view, Set):
+ view = set(iter(view))
+ if isinstance(other, Set):
+ other = set(iter(other))
+ if not isinstance(other, Set):
+ other = set(iter(other))
+ return view | other
+
+
+def _viewbaseset_sub(view, other):
+ if not isinstance(other, Iterable):
+ return NotImplemented
+ if isinstance(view, Set):
+ view = set(iter(view))
+ if isinstance(other, Set):
+ other = set(iter(other))
+ if not isinstance(other, Set):
+ other = set(iter(other))
+ return view - other
+
+
+def _viewbaseset_xor(view, other):
+ if not isinstance(other, Iterable):
+ return NotImplemented
+ if isinstance(view, Set):
+ view = set(iter(view))
+ if isinstance(other, Set):
+ other = set(iter(other))
+ if not isinstance(other, Set):
+ other = set(iter(other))
+ return view ^ other
+
+
+def _itemsview_isdisjoint(view, other):
+ "Return True if two sets have a null intersection."
+ for v in other:
+ if v in view:
+ return False
+ return True
+
+
+def _itemsview_repr(view):
+ lst = []
+ for k, v in view:
+ lst.append("{!r}: {!r}".format(k, v))
+ body = ", ".join(lst)
+ return "{}({})".format(view.__class__.__name__, body)
+
+
+def _keysview_isdisjoint(view, other):
+ "Return True if two sets have a null intersection."
+ for k in other:
+ if k in view:
+ return False
+ return True
+
+
+def _keysview_repr(view):
+ lst = []
+ for k in view:
+ lst.append("{!r}".format(k))
+ body = ", ".join(lst)
+ return "{}({})".format(view.__class__.__name__, body)
+
+
+def _valuesview_repr(view):
+ lst = []
+ for v in view:
+ lst.append("{!r}".format(v))
+ body = ", ".join(lst)
+ return "{}({})".format(view.__class__.__name__, body)
+
+
+def _mdrepr(md):
+ lst = []
+ for k, v in md.items():
+ lst.append("'{}': {!r}".format(k, v))
+ body = ", ".join(lst)
+ return "<{}({})>".format(md.__class__.__name__, body)
diff --git a/third_party/python/multidict/multidict/_multidict_py.py b/third_party/python/multidict/multidict/_multidict_py.py
new file mode 100644
index 0000000000..1ec63da0d5
--- /dev/null
+++ b/third_party/python/multidict/multidict/_multidict_py.py
@@ -0,0 +1,515 @@
+import sys
+from array import array
+from collections import abc
+
+from ._abc import MultiMapping, MutableMultiMapping
+
+_marker = object()
+
+
+class istr(str):
+
+ """Case insensitive str."""
+
+ __is_istr__ = True
+
+
+upstr = istr # for relaxing backward compatibility problems
+
+
+def getversion(md):
+ if not isinstance(md, _Base):
+ raise TypeError("Parameter should be multidict or proxy")
+ return md._impl._version
+
+
+_version = array("Q", [0])
+
+
+class _Impl:
+ __slots__ = ("_items", "_version")
+
+ def __init__(self):
+ self._items = []
+ self.incr_version()
+
+ def incr_version(self):
+ global _version
+ v = _version
+ v[0] += 1
+ self._version = v[0]
+
+ if sys.implementation.name != "pypy":
+
+ def __sizeof__(self):
+ return object.__sizeof__(self) + sys.getsizeof(self._items)
+
+
+class _Base:
+ def _title(self, key):
+ return key
+
+ def getall(self, key, default=_marker):
+ """Return a list of all values matching the key."""
+ identity = self._title(key)
+ res = [v for i, k, v in self._impl._items if i == identity]
+ if res:
+ return res
+ if not res and default is not _marker:
+ return default
+ raise KeyError("Key not found: %r" % key)
+
+ def getone(self, key, default=_marker):
+ """Get first value matching the key."""
+ identity = self._title(key)
+ for i, k, v in self._impl._items:
+ if i == identity:
+ return v
+ if default is not _marker:
+ return default
+ raise KeyError("Key not found: %r" % key)
+
+ # Mapping interface #
+
+ def __getitem__(self, key):
+ return self.getone(key)
+
+ def get(self, key, default=None):
+ """Get first value matching the key.
+
+ The method is alias for .getone().
+ """
+ return self.getone(key, default)
+
+ def __iter__(self):
+ return iter(self.keys())
+
+ def __len__(self):
+ return len(self._impl._items)
+
+ def keys(self):
+ """Return a new view of the dictionary's keys."""
+ return _KeysView(self._impl)
+
+ def items(self):
+ """Return a new view of the dictionary's items *(key, value) pairs)."""
+ return _ItemsView(self._impl)
+
+ def values(self):
+ """Return a new view of the dictionary's values."""
+ return _ValuesView(self._impl)
+
+ def __eq__(self, other):
+ if not isinstance(other, abc.Mapping):
+ return NotImplemented
+ if isinstance(other, _Base):
+ lft = self._impl._items
+ rht = other._impl._items
+ if len(lft) != len(rht):
+ return False
+ for (i1, k2, v1), (i2, k2, v2) in zip(lft, rht):
+ if i1 != i2 or v1 != v2:
+ return False
+ return True
+ if len(self._impl._items) != len(other):
+ return False
+ for k, v in self.items():
+ nv = other.get(k, _marker)
+ if v != nv:
+ return False
+ return True
+
+ def __contains__(self, key):
+ identity = self._title(key)
+ for i, k, v in self._impl._items:
+ if i == identity:
+ return True
+ return False
+
+ def __repr__(self):
+ body = ", ".join("'{}': {!r}".format(k, v) for k, v in self.items())
+ return "<{}({})>".format(self.__class__.__name__, body)
+
+
+class MultiDictProxy(_Base, MultiMapping):
+ """Read-only proxy for MultiDict instance."""
+
+ def __init__(self, arg):
+ if not isinstance(arg, (MultiDict, MultiDictProxy)):
+ raise TypeError(
+ "ctor requires MultiDict or MultiDictProxy instance"
+ ", not {}".format(type(arg))
+ )
+
+ self._impl = arg._impl
+
+ def __reduce__(self):
+ raise TypeError("can't pickle {} objects".format(self.__class__.__name__))
+
+ def copy(self):
+ """Return a copy of itself."""
+ return MultiDict(self.items())
+
+
+class CIMultiDictProxy(MultiDictProxy):
+ """Read-only proxy for CIMultiDict instance."""
+
+ def __init__(self, arg):
+ if not isinstance(arg, (CIMultiDict, CIMultiDictProxy)):
+ raise TypeError(
+ "ctor requires CIMultiDict or CIMultiDictProxy instance"
+ ", not {}".format(type(arg))
+ )
+
+ self._impl = arg._impl
+
+ def _title(self, key):
+ return key.title()
+
+ def copy(self):
+ """Return a copy of itself."""
+ return CIMultiDict(self.items())
+
+
+class MultiDict(_Base, MutableMultiMapping):
+ """Dictionary with the support for duplicate keys."""
+
+ def __init__(self, *args, **kwargs):
+ self._impl = _Impl()
+
+ self._extend(args, kwargs, self.__class__.__name__, self._extend_items)
+
+ if sys.implementation.name != "pypy":
+
+ def __sizeof__(self):
+ return object.__sizeof__(self) + sys.getsizeof(self._impl)
+
+ def __reduce__(self):
+ return (self.__class__, (list(self.items()),))
+
+ def _title(self, key):
+ return key
+
+ def _key(self, key):
+ if isinstance(key, str):
+ return key
+ else:
+ raise TypeError(
+ "MultiDict keys should be either str " "or subclasses of str"
+ )
+
+ def add(self, key, value):
+ identity = self._title(key)
+ self._impl._items.append((identity, self._key(key), value))
+ self._impl.incr_version()
+
+ def copy(self):
+ """Return a copy of itself."""
+ cls = self.__class__
+ return cls(self.items())
+
+ __copy__ = copy
+
+ def extend(self, *args, **kwargs):
+ """Extend current MultiDict with more values.
+
+ This method must be used instead of update.
+ """
+ self._extend(args, kwargs, "extend", self._extend_items)
+
+ def _extend(self, args, kwargs, name, method):
+ if len(args) > 1:
+ raise TypeError(
+ "{} takes at most 1 positional argument"
+ " ({} given)".format(name, len(args))
+ )
+ if args:
+ arg = args[0]
+ if isinstance(args[0], (MultiDict, MultiDictProxy)) and not kwargs:
+ items = arg._impl._items
+ else:
+ if hasattr(arg, "items"):
+ arg = arg.items()
+ if kwargs:
+ arg = list(arg)
+ arg.extend(list(kwargs.items()))
+ items = []
+ for item in arg:
+ if not len(item) == 2:
+ raise TypeError(
+ "{} takes either dict or list of (key, value) "
+ "tuples".format(name)
+ )
+ items.append((self._title(item[0]), self._key(item[0]), item[1]))
+
+ method(items)
+ else:
+ method(
+ [
+ (self._title(key), self._key(key), value)
+ for key, value in kwargs.items()
+ ]
+ )
+
+ def _extend_items(self, items):
+ for identity, key, value in items:
+ self.add(key, value)
+
+ def clear(self):
+ """Remove all items from MultiDict."""
+ self._impl._items.clear()
+ self._impl.incr_version()
+
+ # Mapping interface #
+
+ def __setitem__(self, key, value):
+ self._replace(key, value)
+
+ def __delitem__(self, key):
+ identity = self._title(key)
+ items = self._impl._items
+ found = False
+ for i in range(len(items) - 1, -1, -1):
+ if items[i][0] == identity:
+ del items[i]
+ found = True
+ if not found:
+ raise KeyError(key)
+ else:
+ self._impl.incr_version()
+
+ def setdefault(self, key, default=None):
+ """Return value for key, set value to default if key is not present."""
+ identity = self._title(key)
+ for i, k, v in self._impl._items:
+ if i == identity:
+ return v
+ self.add(key, default)
+ return default
+
+ def popone(self, key, default=_marker):
+ """Remove specified key and return the corresponding value.
+
+ If key is not found, d is returned if given, otherwise
+ KeyError is raised.
+
+ """
+ identity = self._title(key)
+ for i in range(len(self._impl._items)):
+ if self._impl._items[i][0] == identity:
+ value = self._impl._items[i][2]
+ del self._impl._items[i]
+ self._impl.incr_version()
+ return value
+ if default is _marker:
+ raise KeyError(key)
+ else:
+ return default
+
+ pop = popone # type: ignore
+
+ def popall(self, key, default=_marker):
+ """Remove all occurrences of key and return the list of corresponding
+ values.
+
+ If key is not found, default is returned if given, otherwise
+ KeyError is raised.
+
+ """
+ found = False
+ identity = self._title(key)
+ ret = []
+ for i in range(len(self._impl._items) - 1, -1, -1):
+ item = self._impl._items[i]
+ if item[0] == identity:
+ ret.append(item[2])
+ del self._impl._items[i]
+ self._impl.incr_version()
+ found = True
+ if not found:
+ if default is _marker:
+ raise KeyError(key)
+ else:
+ return default
+ else:
+ ret.reverse()
+ return ret
+
+ def popitem(self):
+ """Remove and return an arbitrary (key, value) pair."""
+ if self._impl._items:
+ i = self._impl._items.pop(0)
+ self._impl.incr_version()
+ return i[1], i[2]
+ else:
+ raise KeyError("empty multidict")
+
+ def update(self, *args, **kwargs):
+ """Update the dictionary from *other*, overwriting existing keys."""
+ self._extend(args, kwargs, "update", self._update_items)
+
+ def _update_items(self, items):
+ if not items:
+ return
+ used_keys = {}
+ for identity, key, value in items:
+ start = used_keys.get(identity, 0)
+ for i in range(start, len(self._impl._items)):
+ item = self._impl._items[i]
+ if item[0] == identity:
+ used_keys[identity] = i + 1
+ self._impl._items[i] = (identity, key, value)
+ break
+ else:
+ self._impl._items.append((identity, key, value))
+ used_keys[identity] = len(self._impl._items)
+
+ # drop tails
+ i = 0
+ while i < len(self._impl._items):
+ item = self._impl._items[i]
+ identity = item[0]
+ pos = used_keys.get(identity)
+ if pos is None:
+ i += 1
+ continue
+ if i >= pos:
+ del self._impl._items[i]
+ else:
+ i += 1
+
+ self._impl.incr_version()
+
+ def _replace(self, key, value):
+ key = self._key(key)
+ identity = self._title(key)
+ items = self._impl._items
+
+ for i in range(len(items)):
+ item = items[i]
+ if item[0] == identity:
+ items[i] = (identity, key, value)
+ # i points to last found item
+ rgt = i
+ self._impl.incr_version()
+ break
+ else:
+ self._impl._items.append((identity, key, value))
+ self._impl.incr_version()
+ return
+
+ # remove all tail items
+ i = rgt + 1
+ while i < len(items):
+ item = items[i]
+ if item[0] == identity:
+ del items[i]
+ else:
+ i += 1
+
+
+class CIMultiDict(MultiDict):
+ """Dictionary with the support for duplicate case-insensitive keys."""
+
+ def _title(self, key):
+ return key.title()
+
+
+class _Iter:
+ __slots__ = ("_size", "_iter")
+
+ def __init__(self, size, iterator):
+ self._size = size
+ self._iter = iterator
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ return next(self._iter)
+
+ def __length_hint__(self):
+ return self._size
+
+
+class _ViewBase:
+ def __init__(self, impl):
+ self._impl = impl
+ self._version = impl._version
+
+ def __len__(self):
+ return len(self._impl._items)
+
+
+class _ItemsView(_ViewBase, abc.ItemsView):
+ def __contains__(self, item):
+ assert isinstance(item, tuple) or isinstance(item, list)
+ assert len(item) == 2
+ for i, k, v in self._impl._items:
+ if item[0] == k and item[1] == v:
+ return True
+ return False
+
+ def __iter__(self):
+ return _Iter(len(self), self._iter())
+
+ def _iter(self):
+ for i, k, v in self._impl._items:
+ if self._version != self._impl._version:
+ raise RuntimeError("Dictionary changed during iteration")
+ yield k, v
+
+ def __repr__(self):
+ lst = []
+ for item in self._impl._items:
+ lst.append("{!r}: {!r}".format(item[1], item[2]))
+ body = ", ".join(lst)
+ return "{}({})".format(self.__class__.__name__, body)
+
+
+class _ValuesView(_ViewBase, abc.ValuesView):
+ def __contains__(self, value):
+ for item in self._impl._items:
+ if item[2] == value:
+ return True
+ return False
+
+ def __iter__(self):
+ return _Iter(len(self), self._iter())
+
+ def _iter(self):
+ for item in self._impl._items:
+ if self._version != self._impl._version:
+ raise RuntimeError("Dictionary changed during iteration")
+ yield item[2]
+
+ def __repr__(self):
+ lst = []
+ for item in self._impl._items:
+ lst.append("{!r}".format(item[2]))
+ body = ", ".join(lst)
+ return "{}({})".format(self.__class__.__name__, body)
+
+
+class _KeysView(_ViewBase, abc.KeysView):
+ def __contains__(self, key):
+ for item in self._impl._items:
+ if item[1] == key:
+ return True
+ return False
+
+ def __iter__(self):
+ return _Iter(len(self), self._iter())
+
+ def _iter(self):
+ for item in self._impl._items:
+ if self._version != self._impl._version:
+ raise RuntimeError("Dictionary changed during iteration")
+ yield item[1]
+
+ def __repr__(self):
+ lst = []
+ for item in self._impl._items:
+ lst.append("{!r}".format(item[1]))
+ body = ", ".join(lst)
+ return "{}({})".format(self.__class__.__name__, body)
diff --git a/third_party/python/multidict/multidict/_multilib/defs.h b/third_party/python/multidict/multidict/_multilib/defs.h
new file mode 100644
index 0000000000..c7027c817e
--- /dev/null
+++ b/third_party/python/multidict/multidict/_multilib/defs.h
@@ -0,0 +1,22 @@
+#ifndef _MULTIDICT_DEFS_H
+#define _MULTIDICT_DEFS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_Py_IDENTIFIER(lower);
+
+/* We link this module statically for convenience. If compiled as a shared
+ library instead, some compilers don't allow addresses of Python objects
+ defined in other libraries to be used in static initializers here. The
+ DEFERRED_ADDRESS macro is used to tag the slots where such addresses
+ appear; the module init function must fill in the tagged slots at runtime.
+ The argument is for documentation -- the macro ignores it.
+*/
+#define DEFERRED_ADDRESS(ADDR) 0
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/third_party/python/multidict/multidict/_multilib/dict.h b/third_party/python/multidict/multidict/_multilib/dict.h
new file mode 100644
index 0000000000..3caf83e5b4
--- /dev/null
+++ b/third_party/python/multidict/multidict/_multilib/dict.h
@@ -0,0 +1,24 @@
+#ifndef _MULTIDICT_C_H
+#define _MULTIDICT_C_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct { // 16 or 24 for GC prefix
+ PyObject_HEAD // 16
+ PyObject *weaklist;
+ pair_list_t pairs;
+} MultiDictObject;
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *weaklist;
+ MultiDictObject *md;
+} MultiDictProxyObject;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/third_party/python/multidict/multidict/_multilib/istr.h b/third_party/python/multidict/multidict/_multilib/istr.h
new file mode 100644
index 0000000000..2688f48914
--- /dev/null
+++ b/third_party/python/multidict/multidict/_multilib/istr.h
@@ -0,0 +1,85 @@
+#ifndef _MULTIDICT_ISTR_H
+#define _MULTIDICT_ISTR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ PyUnicodeObject str;
+ PyObject * canonical;
+} istrobject;
+
+PyDoc_STRVAR(istr__doc__, "istr class implementation");
+
+static PyTypeObject istr_type;
+
+static inline void
+istr_dealloc(istrobject *self)
+{
+ Py_XDECREF(self->canonical);
+ PyUnicode_Type.tp_dealloc((PyObject*)self);
+}
+
+static inline PyObject *
+istr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ PyObject *x = NULL;
+ static char *kwlist[] = {"object", "encoding", "errors", 0};
+ PyObject *encoding = NULL;
+ PyObject *errors = NULL;
+ PyObject *s = NULL;
+ PyObject * ret = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO:str",
+ kwlist, &x, &encoding, &errors)) {
+ return NULL;
+ }
+ if (x != NULL && Py_TYPE(x) == &istr_type) {
+ Py_INCREF(x);
+ return x;
+ }
+ ret = PyUnicode_Type.tp_new(type, args, kwds);
+ if (!ret) {
+ goto fail;
+ }
+ s =_PyObject_CallMethodId(ret, &PyId_lower, NULL);
+ if (!s) {
+ goto fail;
+ }
+ ((istrobject*)ret)->canonical = s;
+ s = NULL; /* the reference is stollen by .canonical */
+ return ret;
+fail:
+ Py_XDECREF(ret);
+ return NULL;
+}
+
+static PyTypeObject istr_type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "multidict._multidict.istr",
+ sizeof(istrobject),
+ .tp_dealloc = (destructor)istr_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT
+ | Py_TPFLAGS_BASETYPE
+ | Py_TPFLAGS_UNICODE_SUBCLASS,
+ .tp_doc = istr__doc__,
+ .tp_base = DEFERRED_ADDRESS(&PyUnicode_Type),
+ .tp_new = (newfunc)istr_new,
+};
+
+
+static inline int
+istr_init(void)
+{
+ istr_type.tp_base = &PyUnicode_Type;
+ if (PyType_Ready(&istr_type) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/third_party/python/multidict/multidict/_multilib/iter.h b/third_party/python/multidict/multidict/_multilib/iter.h
new file mode 100644
index 0000000000..4e2e32b387
--- /dev/null
+++ b/third_party/python/multidict/multidict/_multilib/iter.h
@@ -0,0 +1,238 @@
+#ifndef _MULTIDICT_ITER_H
+#define _MULTIDICT_ITER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static PyTypeObject multidict_items_iter_type;
+static PyTypeObject multidict_values_iter_type;
+static PyTypeObject multidict_keys_iter_type;
+
+typedef struct multidict_iter {
+ PyObject_HEAD
+ MultiDictObject *md; // MultiDict or CIMultiDict
+ Py_ssize_t current;
+ uint64_t version;
+} MultidictIter;
+
+static inline void
+_init_iter(MultidictIter *it, MultiDictObject *md)
+{
+ Py_INCREF(md);
+
+ it->md = md;
+ it->current = 0;
+ it->version = pair_list_version(&md->pairs);
+}
+
+static inline PyObject *
+multidict_items_iter_new(MultiDictObject *md)
+{
+ MultidictIter *it = PyObject_GC_New(
+ MultidictIter, &multidict_items_iter_type);
+ if (it == NULL) {
+ return NULL;
+ }
+
+ _init_iter(it, md);
+
+ PyObject_GC_Track(it);
+ return (PyObject *)it;
+}
+
+static inline PyObject *
+multidict_keys_iter_new(MultiDictObject *md)
+{
+ MultidictIter *it = PyObject_GC_New(
+ MultidictIter, &multidict_keys_iter_type);
+ if (it == NULL) {
+ return NULL;
+ }
+
+ _init_iter(it, md);
+
+ PyObject_GC_Track(it);
+ return (PyObject *)it;
+}
+
+static inline PyObject *
+multidict_values_iter_new(MultiDictObject *md)
+{
+ MultidictIter *it = PyObject_GC_New(
+ MultidictIter, &multidict_values_iter_type);
+ if (it == NULL) {
+ return NULL;
+ }
+
+ _init_iter(it, md);
+
+ PyObject_GC_Track(it);
+ return (PyObject *)it;
+}
+
+static inline PyObject *
+multidict_items_iter_iternext(MultidictIter *self)
+{
+ PyObject *key = NULL;
+ PyObject *value = NULL;
+ PyObject *ret = NULL;
+
+ if (self->version != pair_list_version(&self->md->pairs)) {
+ PyErr_SetString(PyExc_RuntimeError, "Dictionary changed during iteration");
+ return NULL;
+ }
+
+ if (!_pair_list_next(&self->md->pairs, &self->current, NULL, &key, &value, NULL)) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+
+ ret = PyTuple_Pack(2, key, value);
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ return ret;
+}
+
+static inline PyObject *
+multidict_values_iter_iternext(MultidictIter *self)
+{
+ PyObject *value = NULL;
+
+ if (self->version != pair_list_version(&self->md->pairs)) {
+ PyErr_SetString(PyExc_RuntimeError, "Dictionary changed during iteration");
+ return NULL;
+ }
+
+ if (!pair_list_next(&self->md->pairs, &self->current, NULL, NULL, &value)) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+
+ Py_INCREF(value);
+
+ return value;
+}
+
+static inline PyObject *
+multidict_keys_iter_iternext(MultidictIter *self)
+{
+ PyObject *key = NULL;
+
+ if (self->version != pair_list_version(&self->md->pairs)) {
+ PyErr_SetString(PyExc_RuntimeError, "Dictionary changed during iteration");
+ return NULL;
+ }
+
+ if (!pair_list_next(&self->md->pairs, &self->current, NULL, &key, NULL)) {
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+
+ Py_INCREF(key);
+
+ return key;
+}
+
+static inline void
+multidict_iter_dealloc(MultidictIter *self)
+{
+ PyObject_GC_UnTrack(self);
+ Py_XDECREF(self->md);
+ PyObject_GC_Del(self);
+}
+
+static inline int
+multidict_iter_traverse(MultidictIter *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->md);
+ return 0;
+}
+
+static inline int
+multidict_iter_clear(MultidictIter *self)
+{
+ Py_CLEAR(self->md);
+ return 0;
+}
+
+static inline PyObject *
+multidict_iter_len(MultidictIter *self)
+{
+ return PyLong_FromLong(pair_list_len(&self->md->pairs));
+}
+
+PyDoc_STRVAR(length_hint_doc,
+ "Private method returning an estimate of len(list(it)).");
+
+static PyMethodDef multidict_iter_methods[] = {
+ {
+ "__length_hint__",
+ (PyCFunction)(void(*)(void))multidict_iter_len,
+ METH_NOARGS,
+ length_hint_doc
+ },
+ {
+ NULL,
+ NULL
+ } /* sentinel */
+};
+
+/***********************************************************************/
+
+static PyTypeObject multidict_items_iter_type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "multidict._multidict._itemsiter", /* tp_name */
+ sizeof(MultidictIter), /* tp_basicsize */
+ .tp_dealloc = (destructor)multidict_iter_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_traverse = (traverseproc)multidict_iter_traverse,
+ .tp_clear = (inquiry)multidict_iter_clear,
+ .tp_iter = PyObject_SelfIter,
+ .tp_iternext = (iternextfunc)multidict_items_iter_iternext,
+ .tp_methods = multidict_iter_methods,
+};
+
+static PyTypeObject multidict_values_iter_type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "multidict._multidict._valuesiter", /* tp_name */
+ sizeof(MultidictIter), /* tp_basicsize */
+ .tp_dealloc = (destructor)multidict_iter_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_traverse = (traverseproc)multidict_iter_traverse,
+ .tp_clear = (inquiry)multidict_iter_clear,
+ .tp_iter = PyObject_SelfIter,
+ .tp_iternext = (iternextfunc)multidict_values_iter_iternext,
+ .tp_methods = multidict_iter_methods,
+};
+
+static PyTypeObject multidict_keys_iter_type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "multidict._multidict._keysiter", /* tp_name */
+ sizeof(MultidictIter), /* tp_basicsize */
+ .tp_dealloc = (destructor)multidict_iter_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_traverse = (traverseproc)multidict_iter_traverse,
+ .tp_clear = (inquiry)multidict_iter_clear,
+ .tp_iter = PyObject_SelfIter,
+ .tp_iternext = (iternextfunc)multidict_keys_iter_iternext,
+ .tp_methods = multidict_iter_methods,
+};
+
+static inline int
+multidict_iter_init()
+{
+ if (PyType_Ready(&multidict_items_iter_type) < 0 ||
+ PyType_Ready(&multidict_values_iter_type) < 0 ||
+ PyType_Ready(&multidict_keys_iter_type) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/third_party/python/multidict/multidict/_multilib/pair_list.h b/third_party/python/multidict/multidict/_multilib/pair_list.h
new file mode 100644
index 0000000000..7eafd215b5
--- /dev/null
+++ b/third_party/python/multidict/multidict/_multilib/pair_list.h
@@ -0,0 +1,1244 @@
+#ifndef _MULTIDICT_PAIR_LIST_H
+#define _MULTIDICT_PAIR_LIST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <string.h>
+#include <stddef.h>
+#include <stdint.h>
+
+typedef PyObject * (*calc_identity_func)(PyObject *key);
+
+typedef struct pair {
+ PyObject *identity; // 8
+ PyObject *key; // 8
+ PyObject *value; // 8
+ Py_hash_t hash; // 8
+} pair_t;
+
+/* Note about the structure size
+With 29 pairs the MultiDict object size is slightly less than 1KiB
+(1000-1008 bytes depending on Python version,
+plus extra 12 bytes for memory allocator internal structures).
+As the result the max reserved size is 1020 bytes at most.
+
+To fit into 512 bytes, the structure can contain only 13 pairs
+which is too small, e.g. https://www.python.org returns 16 headers
+(9 of them are caching proxy information though).
+
+The embedded buffer intention is to fit the vast majority of possible
+HTTP headers into the buffer without allocating an extra memory block.
+*/
+
+#if (PY_VERSION_HEX < 0x03080000)
+#define EMBEDDED_CAPACITY 28
+#else
+#define EMBEDDED_CAPACITY 29
+#endif
+
+typedef struct pair_list { // 40
+ Py_ssize_t capacity; // 8
+ Py_ssize_t size; // 8
+ uint64_t version; // 8
+ calc_identity_func calc_identity; // 8
+ pair_t *pairs; // 8
+ pair_t buffer[EMBEDDED_CAPACITY];
+} pair_list_t;
+
+#define MIN_CAPACITY 63
+#define CAPACITY_STEP 64
+
+/* Global counter used to set ma_version_tag field of dictionary.
+ * It is incremented each time that a dictionary is created and each
+ * time that a dictionary is modified. */
+static uint64_t pair_list_global_version = 0;
+
+#define NEXT_VERSION() (++pair_list_global_version)
+
+
+static inline int
+str_cmp(PyObject *s1, PyObject *s2)
+{
+ PyObject *ret = PyUnicode_RichCompare(s1, s2, Py_EQ);
+ if (ret == Py_True) {
+ Py_DECREF(ret);
+ return 1;
+ }
+ else if (ret == NULL) {
+ return -1;
+ }
+ else {
+ Py_DECREF(ret);
+ return 0;
+ }
+}
+
+
+static inline PyObject *
+key_to_str(PyObject *key)
+{
+ PyObject *ret;
+ PyTypeObject *type = Py_TYPE(key);
+ if (type == &istr_type) {
+ ret = ((istrobject*)key)->canonical;
+ Py_INCREF(ret);
+ return ret;
+ }
+ if (PyUnicode_CheckExact(key)) {
+ Py_INCREF(key);
+ return key;
+ }
+ if (PyUnicode_Check(key)) {
+ return PyObject_Str(key);
+ }
+ PyErr_SetString(PyExc_TypeError,
+ "MultiDict keys should be either str "
+ "or subclasses of str");
+ return NULL;
+}
+
+
+static inline PyObject *
+ci_key_to_str(PyObject *key)
+{
+ PyObject *ret;
+ PyTypeObject *type = Py_TYPE(key);
+ if (type == &istr_type) {
+ ret = ((istrobject*)key)->canonical;
+ Py_INCREF(ret);
+ return ret;
+ }
+ if (PyUnicode_Check(key)) {
+ return _PyObject_CallMethodId(key, &PyId_lower, NULL);
+ }
+ PyErr_SetString(PyExc_TypeError,
+ "CIMultiDict keys should be either str "
+ "or subclasses of str");
+ return NULL;
+}
+
+static inline pair_t *
+pair_list_get(pair_list_t *list, Py_ssize_t i)
+{
+ pair_t *item = list->pairs + i;
+ return item;
+}
+
+
+static inline int
+pair_list_grow(pair_list_t *list)
+{
+ // Grow by one element if needed
+ Py_ssize_t new_capacity;
+ pair_t *new_pairs;
+
+ if (list->size < list->capacity) {
+ return 0;
+ }
+
+ if (list->pairs == list->buffer) {
+ new_pairs = PyMem_New(pair_t, MIN_CAPACITY);
+ memcpy(new_pairs, list->buffer, (size_t)list->capacity * sizeof(pair_t));
+
+ list->pairs = new_pairs;
+ list->capacity = MIN_CAPACITY;
+ return 0;
+ } else {
+ new_capacity = list->capacity + CAPACITY_STEP;
+ new_pairs = PyMem_Resize(list->pairs, pair_t, (size_t)new_capacity);
+
+ if (NULL == new_pairs) {
+ // Resizing error
+ return -1;
+ }
+
+ list->pairs = new_pairs;
+ list->capacity = new_capacity;
+ return 0;
+ }
+}
+
+
+static inline int
+pair_list_shrink(pair_list_t *list)
+{
+ // Shrink by one element if needed.
+ // Optimization is applied to prevent jitter
+ // (grow-shrink-grow-shrink on adding-removing the single element
+ // when the buffer is full).
+ // To prevent this, the buffer is resized if the size is less than the capacity
+ // by 2*CAPACITY_STEP factor.
+ // The switch back to embedded buffer is never performed for both reasons:
+ // the code simplicity and the jitter prevention.
+
+ pair_t *new_pairs;
+ Py_ssize_t new_capacity;
+
+ if (list->capacity - list->size < 2 * CAPACITY_STEP) {
+ return 0;
+ }
+ new_capacity = list->capacity - CAPACITY_STEP;
+ if (new_capacity < MIN_CAPACITY) {
+ return 0;
+ }
+
+ new_pairs = PyMem_Resize(list->pairs, pair_t, (size_t)new_capacity);
+
+ if (NULL == new_pairs) {
+ // Resizing error
+ return -1;
+ }
+
+ list->pairs = new_pairs;
+ list->capacity = new_capacity;
+
+ return 0;
+}
+
+
+static inline int
+_pair_list_init(pair_list_t *list, calc_identity_func calc_identity)
+{
+ list->pairs = list->buffer;
+ list->capacity = EMBEDDED_CAPACITY;
+ list->size = 0;
+ list->version = NEXT_VERSION();
+ list->calc_identity = calc_identity;
+ return 0;
+}
+
+static inline int
+pair_list_init(pair_list_t *list)
+{
+ return _pair_list_init(list, key_to_str);
+}
+
+
+static inline int
+ci_pair_list_init(pair_list_t *list)
+{
+ return _pair_list_init(list, ci_key_to_str);
+}
+
+
+static inline void
+pair_list_dealloc(pair_list_t *list)
+{
+ pair_t *pair;
+ Py_ssize_t pos;
+
+ for (pos = 0; pos < list->size; pos++) {
+ pair = pair_list_get(list, pos);
+
+ Py_XDECREF(pair->identity);
+ Py_XDECREF(pair->key);
+ Py_XDECREF(pair->value);
+ }
+
+ /*
+ Strictly speaking, resetting size and capacity and
+ assigning pairs to buffer is not necessary.
+ Do it to consistency and idemotency.
+ The cleanup doesn't hurt performance.
+ !!!
+ !!! The buffer deletion is crucial though.
+ !!!
+ */
+ list->size = 0;
+ if (list->pairs != list->buffer) {
+ PyMem_Del(list->pairs);
+ list->pairs = list->buffer;
+ list->capacity = EMBEDDED_CAPACITY;
+ }
+}
+
+
+static inline Py_ssize_t
+pair_list_len(pair_list_t *list)
+{
+ return list->size;
+}
+
+
+static inline int
+_pair_list_add_with_hash(pair_list_t *list,
+ PyObject *identity,
+ PyObject *key,
+ PyObject *value,
+ Py_hash_t hash)
+{
+ pair_t *pair;
+
+ if (pair_list_grow(list) < 0) {
+ return -1;
+ }
+
+ pair = pair_list_get(list, list->size);
+
+ Py_INCREF(identity);
+ pair->identity = identity;
+
+ Py_INCREF(key);
+ pair->key = key;
+
+ Py_INCREF(value);
+ pair->value = value;
+
+ pair->hash = hash;
+
+ list->version = NEXT_VERSION();
+ list->size += 1;
+
+ return 0;
+}
+
+
+static inline int
+pair_list_add(pair_list_t *list,
+ PyObject *key,
+ PyObject *value)
+{
+ Py_hash_t hash;
+ PyObject *identity = NULL;
+ int ret;
+
+ identity = list->calc_identity(key);
+ if (identity == NULL) {
+ goto fail;
+ }
+ hash = PyObject_Hash(identity);
+ if (hash == -1) {
+ goto fail;
+ }
+ ret = _pair_list_add_with_hash(list, identity, key, value, hash);
+ Py_DECREF(identity);
+ return ret;
+fail:
+ Py_XDECREF(identity);
+ return -1;
+}
+
+
+static inline int
+pair_list_del_at(pair_list_t *list, Py_ssize_t pos)
+{
+ // return 1 on success, -1 on failure
+ Py_ssize_t tail;
+ pair_t *pair;
+
+ pair = pair_list_get(list, pos);
+ Py_DECREF(pair->identity);
+ Py_DECREF(pair->key);
+ Py_DECREF(pair->value);
+
+ list->size -= 1;
+ list->version = NEXT_VERSION();
+
+ if (list->size == pos) {
+ // remove from tail, no need to shift body
+ return 0;
+ }
+
+ tail = list->size - pos;
+ // TODO: raise an error if tail < 0
+ memmove((void *)pair_list_get(list, pos),
+ (void *)pair_list_get(list, pos + 1),
+ sizeof(pair_t) * (size_t)tail);
+
+ return pair_list_shrink(list);
+}
+
+
+static inline int
+_pair_list_drop_tail(pair_list_t *list, PyObject *identity, Py_hash_t hash,
+ Py_ssize_t pos)
+{
+ // return 1 if deleted, 0 if not found
+ pair_t *pair;
+ int ret;
+ int found = 0;
+
+ if (pos >= list->size) {
+ return 0;
+ }
+
+ for (; pos < list->size; pos++) {
+ pair = pair_list_get(list, pos);
+ if (pair->hash != hash) {
+ continue;
+ }
+ ret = str_cmp(pair->identity, identity);
+ if (ret > 0) {
+ if (pair_list_del_at(list, pos) < 0) {
+ return -1;
+ }
+ found = 1;
+ pos--;
+ }
+ else if (ret == -1) {
+ return -1;
+ }
+ }
+
+ return found;
+}
+
+static inline int
+_pair_list_del_hash(pair_list_t *list, PyObject *identity,
+ PyObject *key, Py_hash_t hash)
+{
+ int ret = _pair_list_drop_tail(list, identity, hash, 0);
+
+ if (ret < 0) {
+ return -1;
+ }
+ else if (ret == 0) {
+ PyErr_SetObject(PyExc_KeyError, key);
+ return -1;
+ }
+ else {
+ list->version = NEXT_VERSION();
+ return 0;
+ }
+}
+
+
+static inline int
+pair_list_del(pair_list_t *list, PyObject *key)
+{
+ PyObject *identity = NULL;
+ Py_hash_t hash;
+ int ret;
+
+ identity = list->calc_identity(key);
+ if (identity == NULL) {
+ goto fail;
+ }
+
+ hash = PyObject_Hash(identity);
+ if (hash == -1) {
+ goto fail;
+ }
+
+ ret = _pair_list_del_hash(list, identity, key, hash);
+ Py_DECREF(identity);
+ return ret;
+fail:
+ Py_XDECREF(identity);
+ return -1;
+}
+
+
+static inline uint64_t
+pair_list_version(pair_list_t *list)
+{
+ return list->version;
+}
+
+
+static inline int
+_pair_list_next(pair_list_t *list, Py_ssize_t *ppos, PyObject **pidentity,
+ PyObject **pkey, PyObject **pvalue, Py_hash_t *phash)
+{
+ pair_t *pair;
+
+ if (*ppos >= list->size) {
+ return 0;
+ }
+
+ pair = pair_list_get(list, *ppos);
+
+ if (pidentity) {
+ *pidentity = pair->identity;
+ }
+ if (pkey) {
+ *pkey = pair->key;
+ }
+ if (pvalue) {
+ *pvalue = pair->value;
+ }
+ if (phash) {
+ *phash = pair->hash;
+ }
+
+ *ppos += 1;
+ return 1;
+}
+
+
+static inline int
+pair_list_next(pair_list_t *list, Py_ssize_t *ppos, PyObject **pidentity,
+ PyObject **pkey, PyObject **pvalue)
+{
+ Py_hash_t hash;
+ return _pair_list_next(list, ppos, pidentity, pkey, pvalue, &hash);
+}
+
+
+static inline int
+pair_list_contains(pair_list_t *list, PyObject *key)
+{
+ Py_hash_t hash1, hash2;
+ Py_ssize_t pos = 0;
+ PyObject *ident = NULL;
+ PyObject *identity = NULL;
+ int tmp;
+
+ ident = list->calc_identity(key);
+ if (ident == NULL) {
+ goto fail;
+ }
+
+ hash1 = PyObject_Hash(ident);
+ if (hash1 == -1) {
+ goto fail;
+ }
+
+ while (_pair_list_next(list, &pos, &identity, NULL, NULL, &hash2)) {
+ if (hash1 != hash2) {
+ continue;
+ }
+ tmp = str_cmp(ident, identity);
+ if (tmp > 0) {
+ Py_DECREF(ident);
+ return 1;
+ }
+ else if (tmp < 0) {
+ goto fail;
+ }
+ }
+
+ Py_DECREF(ident);
+ return 0;
+fail:
+ Py_XDECREF(ident);
+ return -1;
+}
+
+
+static inline PyObject *
+pair_list_get_one(pair_list_t *list, PyObject *key)
+{
+ Py_hash_t hash1, hash2;
+ Py_ssize_t pos = 0;
+ PyObject *ident = NULL;
+ PyObject *identity = NULL;
+ PyObject *value = NULL;
+ int tmp;
+
+ ident = list->calc_identity(key);
+ if (ident == NULL) {
+ goto fail;
+ }
+
+ hash1 = PyObject_Hash(ident);
+ if (hash1 == -1) {
+ goto fail;
+ }
+
+ while (_pair_list_next(list, &pos, &identity, NULL, &value, &hash2)) {
+ if (hash1 != hash2) {
+ continue;
+ }
+ tmp = str_cmp(ident, identity);
+ if (tmp > 0) {
+ Py_INCREF(value);
+ Py_DECREF(ident);
+ return value;
+ }
+ else if (tmp < 0) {
+ goto fail;
+ }
+ }
+
+ Py_DECREF(ident);
+ PyErr_SetObject(PyExc_KeyError, key);
+ return NULL;
+fail:
+ Py_XDECREF(ident);
+ return NULL;
+}
+
+
+static inline PyObject *
+pair_list_get_all(pair_list_t *list, PyObject *key)
+{
+ Py_hash_t hash1, hash2;
+ Py_ssize_t pos = 0;
+ PyObject *ident = NULL;
+ PyObject *identity = NULL;
+ PyObject *value = NULL;
+ PyObject *res = NULL;
+ int tmp;
+
+ ident = list->calc_identity(key);
+ if (ident == NULL) {
+ goto fail;
+ }
+
+ hash1 = PyObject_Hash(ident);
+ if (hash1 == -1) {
+ goto fail;
+ }
+
+ while (_pair_list_next(list, &pos, &identity, NULL, &value, &hash2)) {
+ if (hash1 != hash2) {
+ continue;
+ }
+ tmp = str_cmp(ident, identity);
+ if (tmp > 0) {
+ if (res == NULL) {
+ res = PyList_New(1);
+ if (res == NULL) {
+ goto fail;
+ }
+ if (PyList_SetItem(res, 0, value) < 0) {
+ goto fail;
+ }
+ Py_INCREF(value);
+ }
+ else if (PyList_Append(res, value) < 0) {
+ goto fail;
+ }
+ }
+ else if (tmp < 0) {
+ goto fail;
+ }
+ }
+
+ if (res == NULL) {
+ PyErr_SetObject(PyExc_KeyError, key);
+ }
+ Py_DECREF(ident);
+ return res;
+
+fail:
+ Py_XDECREF(ident);
+ Py_XDECREF(res);
+ return NULL;
+}
+
+
+static inline PyObject *
+pair_list_set_default(pair_list_t *list, PyObject *key, PyObject *value)
+{
+ Py_hash_t hash1, hash2;
+ Py_ssize_t pos = 0;
+ PyObject *ident = NULL;
+ PyObject *identity = NULL;
+ PyObject *value2 = NULL;
+ int tmp;
+
+ ident = list->calc_identity(key);
+ if (ident == NULL) {
+ goto fail;
+ }
+
+ hash1 = PyObject_Hash(ident);
+ if (hash1 == -1) {
+ goto fail;
+ }
+
+ while (_pair_list_next(list, &pos, &identity, NULL, &value2, &hash2)) {
+ if (hash1 != hash2) {
+ continue;
+ }
+ tmp = str_cmp(ident, identity);
+ if (tmp > 0) {
+ Py_INCREF(value2);
+ Py_DECREF(ident);
+ return value2;
+ }
+ else if (tmp < 0) {
+ goto fail;
+ }
+ }
+
+ if (_pair_list_add_with_hash(list, ident, key, value, hash1) < 0) {
+ goto fail;
+ }
+
+ Py_INCREF(value);
+ Py_DECREF(ident);
+ return value;
+fail:
+ Py_XDECREF(ident);
+ return NULL;
+}
+
+
+static inline PyObject *
+pair_list_pop_one(pair_list_t *list, PyObject *key)
+{
+ pair_t *pair;
+
+ Py_hash_t hash;
+ Py_ssize_t pos;
+ PyObject *value = NULL;
+ int tmp;
+ PyObject *ident = NULL;
+
+ ident = list->calc_identity(key);
+ if (ident == NULL) {
+ goto fail;
+ }
+
+ hash = PyObject_Hash(ident);
+ if (hash == -1) {
+ goto fail;
+ }
+
+ for (pos=0; pos < list->size; pos++) {
+ pair = pair_list_get(list, pos);
+ if (pair->hash != hash) {
+ continue;
+ }
+ tmp = str_cmp(ident, pair->identity);
+ if (tmp > 0) {
+ value = pair->value;
+ Py_INCREF(value);
+ if (pair_list_del_at(list, pos) < 0) {
+ goto fail;
+ }
+ Py_DECREF(ident);
+ return value;
+ }
+ else if (tmp < 0) {
+ goto fail;
+ }
+ }
+
+ PyErr_SetObject(PyExc_KeyError, key);
+ goto fail;
+
+fail:
+ Py_XDECREF(value);
+ Py_XDECREF(ident);
+ return NULL;
+}
+
+
+static inline PyObject *
+pair_list_pop_all(pair_list_t *list, PyObject *key)
+{
+ Py_hash_t hash;
+ Py_ssize_t pos;
+ pair_t *pair;
+ int tmp;
+ PyObject *res = NULL;
+ PyObject *ident = NULL;
+
+ ident = list->calc_identity(key);
+ if (ident == NULL) {
+ goto fail;
+ }
+
+ hash = PyObject_Hash(ident);
+ if (hash == -1) {
+ goto fail;
+ }
+
+ if (list->size == 0) {
+ PyErr_SetObject(PyExc_KeyError, ident);
+ goto fail;
+ }
+
+ for (pos = list->size - 1; pos >= 0; pos--) {
+ pair = pair_list_get(list, pos);
+ if (hash != pair->hash) {
+ continue;
+ }
+ tmp = str_cmp(ident, pair->identity);
+ if (tmp > 0) {
+ if (res == NULL) {
+ res = PyList_New(1);
+ if (res == NULL) {
+ goto fail;
+ }
+ if (PyList_SetItem(res, 0, pair->value) < 0) {
+ goto fail;
+ }
+ Py_INCREF(pair->value);
+ } else if (PyList_Append(res, pair->value) < 0) {
+ goto fail;
+ }
+ if (pair_list_del_at(list, pos) < 0) {
+ goto fail;
+ }
+ }
+ else if (tmp < 0) {
+ goto fail;
+ }
+ }
+
+ if (res == NULL) {
+ PyErr_SetObject(PyExc_KeyError, key);
+ } else if (PyList_Reverse(res) < 0) {
+ goto fail;
+ }
+ Py_DECREF(ident);
+ return res;
+
+fail:
+ Py_XDECREF(ident);
+ Py_XDECREF(res);
+ return NULL;
+}
+
+
+static inline PyObject *
+pair_list_pop_item(pair_list_t *list)
+{
+ PyObject *ret;
+ pair_t *pair;
+
+ if (list->size == 0) {
+ PyErr_SetString(PyExc_KeyError, "empty multidict");
+ return NULL;
+ }
+
+ pair = pair_list_get(list, 0);
+ ret = PyTuple_Pack(2, pair->key, pair->value);
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ if (pair_list_del_at(list, 0) < 0) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+
+static inline int
+pair_list_replace(pair_list_t *list, PyObject * key, PyObject *value)
+{
+ pair_t *pair;
+
+ Py_ssize_t pos;
+ int tmp;
+ int found = 0;
+
+ PyObject *identity = NULL;
+ Py_hash_t hash;
+
+ identity = list->calc_identity(key);
+ if (identity == NULL) {
+ goto fail;
+ }
+
+ hash = PyObject_Hash(identity);
+ if (hash == -1) {
+ goto fail;
+ }
+
+
+ for (pos = 0; pos < list->size; pos++) {
+ pair = pair_list_get(list, pos);
+ if (hash != pair->hash) {
+ continue;
+ }
+ tmp = str_cmp(identity, pair->identity);
+ if (tmp > 0) {
+ found = 1;
+ Py_INCREF(key);
+ Py_DECREF(pair->key);
+ pair->key = key;
+ Py_INCREF(value);
+ Py_DECREF(pair->value);
+ pair->value = value;
+ break;
+ }
+ else if (tmp < 0) {
+ goto fail;
+ }
+ }
+
+ if (!found) {
+ if (_pair_list_add_with_hash(list, identity, key, value, hash) < 0) {
+ goto fail;
+ }
+ Py_DECREF(identity);
+ return 0;
+ }
+ else {
+ list->version = NEXT_VERSION();
+ if (_pair_list_drop_tail(list, identity, hash, pos+1) < 0) {
+ goto fail;
+ }
+ Py_DECREF(identity);
+ return 0;
+ }
+fail:
+ Py_XDECREF(identity);
+ return -1;
+}
+
+
+static inline int
+_dict_set_number(PyObject *dict, PyObject *key, Py_ssize_t num)
+{
+ PyObject *tmp = PyLong_FromSsize_t(num);
+ if (tmp == NULL) {
+ return -1;
+ }
+
+ if (PyDict_SetItem(dict, key, tmp) < 0) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static inline int
+_pair_list_post_update(pair_list_t *list, PyObject* used_keys, Py_ssize_t pos)
+{
+ pair_t *pair;
+ PyObject *tmp;
+ Py_ssize_t num;
+
+ for (; pos < list->size; pos++) {
+ pair = pair_list_get(list, pos);
+ tmp = PyDict_GetItem(used_keys, pair->identity);
+ if (tmp == NULL) {
+ // not found
+ continue;
+ }
+
+ num = PyLong_AsSsize_t(tmp);
+ if (num == -1) {
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_RuntimeError, "invalid internal state");
+ }
+ return -1;
+ }
+
+ if (pos >= num) {
+ // del self[pos]
+ if (pair_list_del_at(list, pos) < 0) {
+ return -1;
+ }
+ pos--;
+ }
+ }
+
+ list->version = NEXT_VERSION();
+ return 0;
+}
+
+// TODO: need refactoring function name
+static inline int
+_pair_list_update(pair_list_t *list, PyObject *key,
+ PyObject *value, PyObject *used_keys,
+ PyObject *identity, Py_hash_t hash)
+{
+ PyObject *item = NULL;
+ pair_t *pair = NULL;
+ Py_ssize_t pos;
+ int found;
+ int ident_cmp_res;
+
+ item = PyDict_GetItem(used_keys, identity);
+ if (item == NULL) {
+ pos = 0;
+ }
+ else {
+ pos = PyLong_AsSsize_t(item);
+ if (pos == -1) {
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_RuntimeError, "invalid internal state");
+ }
+ return -1;
+ }
+ }
+
+ found = 0;
+ for (; pos < list->size; pos++) {
+ pair = pair_list_get(list, pos);
+ if (pair->hash != hash) {
+ continue;
+ }
+
+ ident_cmp_res = str_cmp(pair->identity, identity);
+ if (ident_cmp_res > 0) {
+ Py_INCREF(key);
+ Py_DECREF(pair->key);
+ pair->key = key;
+
+ Py_INCREF(value);
+ Py_DECREF(pair->value);
+ pair->value = value;
+
+ if (_dict_set_number(used_keys, pair->identity, pos + 1) < 0) {
+ return -1;
+ }
+
+ found = 1;
+ break;
+ }
+ else if (ident_cmp_res < 0) {
+ return -1;
+ }
+ }
+
+ if (!found) {
+ if (_pair_list_add_with_hash(list, identity, key, value, hash) < 0) {
+ return -1;
+ }
+ if (_dict_set_number(used_keys, identity, list->size) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static inline int
+pair_list_update(pair_list_t *list, pair_list_t *other)
+{
+ PyObject *used_keys = NULL;
+ pair_t *pair = NULL;
+
+ Py_ssize_t pos;
+
+ if (other->size == 0) {
+ return 0;
+ }
+
+ used_keys = PyDict_New();
+ if (used_keys == NULL) {
+ return -1;
+ }
+
+ for (pos = 0; pos < other->size; pos++) {
+ pair = pair_list_get(other, pos);
+ if (_pair_list_update(list, pair->key, pair->value, used_keys,
+ pair->identity, pair->hash) < 0) {
+ goto fail;
+ }
+ }
+
+ if (_pair_list_post_update(list, used_keys, 0) < 0) {
+ goto fail;
+ }
+
+ Py_DECREF(used_keys);
+ return 0;
+
+fail:
+ Py_XDECREF(used_keys);
+ return -1;
+}
+
+
+static inline int
+pair_list_update_from_seq(pair_list_t *list, PyObject *seq)
+{
+ PyObject *it = NULL; // iter(seq)
+ PyObject *fast = NULL; // item as a 2-tuple or 2-list
+ PyObject *item = NULL; // seq[i]
+ PyObject *used_keys = NULL; // dict(<Identitty: Pos>)
+
+ PyObject *key = NULL;
+ PyObject *value = NULL;
+ PyObject *identity = NULL;
+
+ Py_hash_t hash;
+
+ Py_ssize_t i;
+ Py_ssize_t n;
+
+ it = PyObject_GetIter(seq);
+ if (it == NULL) {
+ return -1;
+ }
+
+ used_keys = PyDict_New();
+ if (used_keys == NULL) {
+ goto fail_1;
+ }
+
+ for (i = 0; ; ++i) { // i - index into seq of current element
+ fast = NULL;
+ item = PyIter_Next(it);
+ if (item == NULL) {
+ if (PyErr_Occurred()) {
+ goto fail_1;
+ }
+ break;
+ }
+
+ // Convert item to sequence, and verify length 2.
+ fast = PySequence_Fast(item, "");
+ if (fast == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Format(PyExc_TypeError,
+ "multidict cannot convert sequence element #%zd"
+ " to a sequence",
+ i);
+ }
+ goto fail_1;
+ }
+
+ n = PySequence_Fast_GET_SIZE(fast);
+ if (n != 2) {
+ PyErr_Format(PyExc_ValueError,
+ "multidict update sequence element #%zd "
+ "has length %zd; 2 is required",
+ i, n);
+ goto fail_1;
+ }
+
+ key = PySequence_Fast_GET_ITEM(fast, 0);
+ value = PySequence_Fast_GET_ITEM(fast, 1);
+ Py_INCREF(key);
+ Py_INCREF(value);
+
+ identity = list->calc_identity(key);
+ if (identity == NULL) {
+ goto fail_1;
+ }
+
+ hash = PyObject_Hash(identity);
+ if (hash == -1) {
+ goto fail_1;
+ }
+
+ if (_pair_list_update(list, key, value, used_keys, identity, hash) < 0) {
+ goto fail_1;
+ }
+
+ Py_DECREF(key);
+ Py_DECREF(value);
+ Py_DECREF(fast);
+ Py_DECREF(item);
+ Py_DECREF(identity);
+ }
+
+ if (_pair_list_post_update(list, used_keys, 0) < 0) {
+ goto fail_2;
+ }
+
+ Py_DECREF(it);
+ Py_DECREF(used_keys);
+ return 0;
+
+fail_1:
+ Py_XDECREF(key);
+ Py_XDECREF(value);
+ Py_XDECREF(fast);
+ Py_XDECREF(item);
+ Py_XDECREF(identity);
+
+fail_2:
+ Py_XDECREF(it);
+ Py_XDECREF(used_keys);
+ return -1;
+}
+
+static inline int
+pair_list_eq_to_mapping(pair_list_t *list, PyObject *other)
+{
+ PyObject *key = NULL;
+ PyObject *avalue = NULL;
+ PyObject *bvalue;
+
+ Py_ssize_t pos, other_len;
+
+ int eq;
+
+ if (!PyMapping_Check(other)) {
+ PyErr_Format(PyExc_TypeError,
+ "other argument must be a mapping, not %s",
+ Py_TYPE(other)->tp_name);
+ return -1;
+ }
+
+ other_len = PyMapping_Size(other);
+ if (other_len < 0) {
+ return -1;
+ }
+ if (pair_list_len(list) != other_len) {
+ return 0;
+ }
+
+ pos = 0;
+ while (pair_list_next(list, &pos, NULL, &key, &avalue)) {
+ bvalue = PyObject_GetItem(other, key);
+ if (bvalue == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_KeyError)) {
+ PyErr_Clear();
+ return 0;
+ }
+ return -1;
+ }
+
+ eq = PyObject_RichCompareBool(avalue, bvalue, Py_EQ);
+ Py_DECREF(bvalue);
+
+ if (eq <= 0) {
+ return eq;
+ }
+ }
+
+ return 1;
+}
+
+
+/***********************************************************************/
+
+static inline int
+pair_list_traverse(pair_list_t *list, visitproc visit, void *arg)
+{
+ pair_t *pair = NULL;
+ Py_ssize_t pos;
+
+ for (pos = 0; pos < list->size; pos++) {
+ pair = pair_list_get(list, pos);
+ // Don't need traverse the identity: it is a terminal
+ Py_VISIT(pair->key);
+ Py_VISIT(pair->value);
+ }
+
+ return 0;
+}
+
+
+static inline int
+pair_list_clear(pair_list_t *list)
+{
+ pair_t *pair = NULL;
+ Py_ssize_t pos;
+
+ if (list->size == 0) {
+ return 0;
+ }
+
+ list->version = NEXT_VERSION();
+ for (pos = 0; pos < list->size; pos++) {
+ pair = pair_list_get(list, pos);
+ Py_CLEAR(pair->key);
+ Py_CLEAR(pair->identity);
+ Py_CLEAR(pair->value);
+ }
+ list->size = 0;
+ if (list->pairs != list->buffer) {
+ PyMem_Del(list->pairs);
+ list->pairs = list->buffer;
+ }
+
+ return 0;
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/third_party/python/multidict/multidict/_multilib/views.h b/third_party/python/multidict/multidict/_multilib/views.h
new file mode 100644
index 0000000000..5b1ebfe77c
--- /dev/null
+++ b/third_party/python/multidict/multidict/_multilib/views.h
@@ -0,0 +1,464 @@
+#ifndef _MULTIDICT_VIEWS_H
+#define _MULTIDICT_VIEWS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static PyTypeObject multidict_itemsview_type;
+static PyTypeObject multidict_valuesview_type;
+static PyTypeObject multidict_keysview_type;
+
+static PyObject *viewbaseset_richcmp_func;
+static PyObject *viewbaseset_and_func;
+static PyObject *viewbaseset_or_func;
+static PyObject *viewbaseset_sub_func;
+static PyObject *viewbaseset_xor_func;
+
+static PyObject *abc_itemsview_register_func;
+static PyObject *abc_keysview_register_func;
+static PyObject *abc_valuesview_register_func;
+
+static PyObject *itemsview_isdisjoint_func;
+static PyObject *itemsview_repr_func;
+
+static PyObject *keysview_repr_func;
+static PyObject *keysview_isdisjoint_func;
+
+static PyObject *valuesview_repr_func;
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *md;
+} _Multidict_ViewObject;
+
+
+/********** Base **********/
+
+static inline void
+_init_view(_Multidict_ViewObject *self, PyObject *md)
+{
+ Py_INCREF(md);
+ self->md = md;
+}
+
+static inline void
+multidict_view_dealloc(_Multidict_ViewObject *self)
+{
+ PyObject_GC_UnTrack(self);
+ Py_XDECREF(self->md);
+ PyObject_GC_Del(self);
+}
+
+static inline int
+multidict_view_traverse(_Multidict_ViewObject *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->md);
+ return 0;
+}
+
+static inline int
+multidict_view_clear(_Multidict_ViewObject *self)
+{
+ Py_CLEAR(self->md);
+ return 0;
+}
+
+static inline Py_ssize_t
+multidict_view_len(_Multidict_ViewObject *self)
+{
+ return pair_list_len(&((MultiDictObject*)self->md)->pairs);
+}
+
+static inline PyObject *
+multidict_view_richcompare(PyObject *self, PyObject *other, int op)
+{
+ PyObject *ret;
+ PyObject *op_obj = PyLong_FromLong(op);
+ if (op_obj == NULL) {
+ return NULL;
+ }
+ ret = PyObject_CallFunctionObjArgs(
+ viewbaseset_richcmp_func, self, other, op_obj, NULL);
+ Py_DECREF(op_obj);
+ return ret;
+}
+
+static inline PyObject *
+multidict_view_and(PyObject *self, PyObject *other)
+{
+ return PyObject_CallFunctionObjArgs(
+ viewbaseset_and_func, self, other, NULL);
+}
+
+static inline PyObject *
+multidict_view_or(PyObject *self, PyObject *other)
+{
+ return PyObject_CallFunctionObjArgs(
+ viewbaseset_or_func, self, other, NULL);
+}
+
+static inline PyObject *
+multidict_view_sub(PyObject *self, PyObject *other)
+{
+ return PyObject_CallFunctionObjArgs(
+ viewbaseset_sub_func, self, other, NULL);
+}
+
+static inline PyObject *
+multidict_view_xor(PyObject *self, PyObject *other)
+{
+ return PyObject_CallFunctionObjArgs(
+ viewbaseset_xor_func, self, other, NULL);
+}
+
+static PyNumberMethods multidict_view_as_number = {
+ .nb_subtract = (binaryfunc)multidict_view_sub,
+ .nb_and = (binaryfunc)multidict_view_and,
+ .nb_xor = (binaryfunc)multidict_view_xor,
+ .nb_or = (binaryfunc)multidict_view_or,
+};
+
+/********** Items **********/
+
+static inline PyObject *
+multidict_itemsview_new(PyObject *md)
+{
+ _Multidict_ViewObject *mv = PyObject_GC_New(
+ _Multidict_ViewObject, &multidict_itemsview_type);
+ if (mv == NULL) {
+ return NULL;
+ }
+
+ _init_view(mv, md);
+
+ PyObject_GC_Track(mv);
+ return (PyObject *)mv;
+}
+
+static inline PyObject *
+multidict_itemsview_iter(_Multidict_ViewObject *self)
+{
+ return multidict_items_iter_new((MultiDictObject*)self->md);
+}
+
+static inline PyObject *
+multidict_itemsview_repr(_Multidict_ViewObject *self)
+{
+ return PyObject_CallFunctionObjArgs(
+ itemsview_repr_func, self, NULL);
+}
+
+static inline PyObject *
+multidict_itemsview_isdisjoint(_Multidict_ViewObject *self, PyObject *other)
+{
+ return PyObject_CallFunctionObjArgs(
+ itemsview_isdisjoint_func, self, other, NULL);
+}
+
+PyDoc_STRVAR(itemsview_isdisjoint_doc,
+ "Return True if two sets have a null intersection.");
+
+static PyMethodDef multidict_itemsview_methods[] = {
+ {
+ "isdisjoint",
+ (PyCFunction)multidict_itemsview_isdisjoint,
+ METH_O,
+ itemsview_isdisjoint_doc
+ },
+ {
+ NULL,
+ NULL
+ } /* sentinel */
+};
+
+static inline int
+multidict_itemsview_contains(_Multidict_ViewObject *self, PyObject *obj)
+{
+ PyObject *akey = NULL,
+ *aval = NULL,
+ *bkey = NULL,
+ *bval = NULL,
+ *iter = NULL,
+ *item = NULL;
+ int ret1, ret2;
+
+ if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) {
+ return 0;
+ }
+
+ bkey = PyTuple_GET_ITEM(obj, 0);
+ bval = PyTuple_GET_ITEM(obj, 1);
+
+ iter = multidict_itemsview_iter(self);
+ if (iter == NULL) {
+ return 0;
+ }
+
+ while ((item = PyIter_Next(iter)) != NULL) {
+ akey = PyTuple_GET_ITEM(item, 0);
+ aval = PyTuple_GET_ITEM(item, 1);
+
+ ret1 = PyObject_RichCompareBool(akey, bkey, Py_EQ);
+ if (ret1 < 0) {
+ Py_DECREF(iter);
+ Py_DECREF(item);
+ return -1;
+ }
+ ret2 = PyObject_RichCompareBool(aval, bval, Py_EQ);
+ if (ret2 < 0) {
+ Py_DECREF(iter);
+ Py_DECREF(item);
+ return -1;
+ }
+ if (ret1 > 0 && ret2 > 0)
+ {
+ Py_DECREF(iter);
+ Py_DECREF(item);
+ return 1;
+ }
+
+ Py_DECREF(item);
+ }
+
+ Py_DECREF(iter);
+
+ if (PyErr_Occurred()) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static PySequenceMethods multidict_itemsview_as_sequence = {
+ .sq_length = (lenfunc)multidict_view_len,
+ .sq_contains = (objobjproc)multidict_itemsview_contains,
+};
+
+static PyTypeObject multidict_itemsview_type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "multidict._multidict._ItemsView", /* tp_name */
+ sizeof(_Multidict_ViewObject), /* tp_basicsize */
+ .tp_dealloc = (destructor)multidict_view_dealloc,
+ .tp_repr = (reprfunc)multidict_itemsview_repr,
+ .tp_as_number = &multidict_view_as_number,
+ .tp_as_sequence = &multidict_itemsview_as_sequence,
+ .tp_getattro = PyObject_GenericGetAttr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_traverse = (traverseproc)multidict_view_traverse,
+ .tp_clear = (inquiry)multidict_view_clear,
+ .tp_richcompare = multidict_view_richcompare,
+ .tp_iter = (getiterfunc)multidict_itemsview_iter,
+ .tp_methods = multidict_itemsview_methods,
+};
+
+
+/********** Keys **********/
+
+static inline PyObject *
+multidict_keysview_new(PyObject *md)
+{
+ _Multidict_ViewObject *mv = PyObject_GC_New(
+ _Multidict_ViewObject, &multidict_keysview_type);
+ if (mv == NULL) {
+ return NULL;
+ }
+
+ _init_view(mv, md);
+
+ PyObject_GC_Track(mv);
+ return (PyObject *)mv;
+}
+
+static inline PyObject *
+multidict_keysview_iter(_Multidict_ViewObject *self)
+{
+ return multidict_keys_iter_new(((MultiDictObject*)self->md));
+}
+
+static inline PyObject *
+multidict_keysview_repr(_Multidict_ViewObject *self)
+{
+ return PyObject_CallFunctionObjArgs(
+ keysview_repr_func, self, NULL);
+}
+
+static inline PyObject *
+multidict_keysview_isdisjoint(_Multidict_ViewObject *self, PyObject *other)
+{
+ return PyObject_CallFunctionObjArgs(
+ keysview_isdisjoint_func, self, other, NULL);
+}
+
+PyDoc_STRVAR(keysview_isdisjoint_doc,
+ "Return True if two sets have a null intersection.");
+
+static PyMethodDef multidict_keysview_methods[] = {
+ {
+ "isdisjoint",
+ (PyCFunction)multidict_keysview_isdisjoint,
+ METH_O,
+ keysview_isdisjoint_doc
+ },
+ {
+ NULL,
+ NULL
+ } /* sentinel */
+};
+
+static inline int
+multidict_keysview_contains(_Multidict_ViewObject *self, PyObject *key)
+{
+ return pair_list_contains(&((MultiDictObject*)self->md)->pairs, key);
+}
+
+static PySequenceMethods multidict_keysview_as_sequence = {
+ .sq_length = (lenfunc)multidict_view_len,
+ .sq_contains = (objobjproc)multidict_keysview_contains,
+};
+
+static PyTypeObject multidict_keysview_type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "multidict._multidict._KeysView", /* tp_name */
+ sizeof(_Multidict_ViewObject), /* tp_basicsize */
+ .tp_dealloc = (destructor)multidict_view_dealloc,
+ .tp_repr = (reprfunc)multidict_keysview_repr,
+ .tp_as_number = &multidict_view_as_number,
+ .tp_as_sequence = &multidict_keysview_as_sequence,
+ .tp_getattro = PyObject_GenericGetAttr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_traverse = (traverseproc)multidict_view_traverse,
+ .tp_clear = (inquiry)multidict_view_clear,
+ .tp_richcompare = multidict_view_richcompare,
+ .tp_iter = (getiterfunc)multidict_keysview_iter,
+ .tp_methods = multidict_keysview_methods,
+};
+
+
+/********** Values **********/
+
+static inline PyObject *
+multidict_valuesview_new(PyObject *md)
+{
+ _Multidict_ViewObject *mv = PyObject_GC_New(
+ _Multidict_ViewObject, &multidict_valuesview_type);
+ if (mv == NULL) {
+ return NULL;
+ }
+
+ _init_view(mv, md);
+
+ PyObject_GC_Track(mv);
+ return (PyObject *)mv;
+}
+
+static inline PyObject *
+multidict_valuesview_iter(_Multidict_ViewObject *self)
+{
+ return multidict_values_iter_new(((MultiDictObject*)self->md));
+}
+
+static inline PyObject *
+multidict_valuesview_repr(_Multidict_ViewObject *self)
+{
+ return PyObject_CallFunctionObjArgs(
+ valuesview_repr_func, self, NULL);
+}
+
+static PySequenceMethods multidict_valuesview_as_sequence = {
+ .sq_length = (lenfunc)multidict_view_len,
+};
+
+static PyTypeObject multidict_valuesview_type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "multidict._multidict._ValuesView", /* tp_name */
+ sizeof(_Multidict_ViewObject), /* tp_basicsize */
+ .tp_dealloc = (destructor)multidict_view_dealloc,
+ .tp_repr = (reprfunc)multidict_valuesview_repr,
+ .tp_as_sequence = &multidict_valuesview_as_sequence,
+ .tp_getattro = PyObject_GenericGetAttr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_traverse = (traverseproc)multidict_view_traverse,
+ .tp_clear = (inquiry)multidict_view_clear,
+ .tp_iter = (getiterfunc)multidict_valuesview_iter,
+};
+
+
+static inline int
+multidict_views_init()
+{
+ PyObject *reg_func_call_result = NULL;
+ PyObject *module = PyImport_ImportModule("multidict._multidict_base");
+ if (module == NULL) {
+ goto fail;
+ }
+
+#define GET_MOD_ATTR(VAR, NAME) \
+ VAR = PyObject_GetAttrString(module, NAME); \
+ if (VAR == NULL) { \
+ goto fail; \
+ }
+
+ GET_MOD_ATTR(viewbaseset_richcmp_func, "_viewbaseset_richcmp");
+ GET_MOD_ATTR(viewbaseset_and_func, "_viewbaseset_and");
+ GET_MOD_ATTR(viewbaseset_or_func, "_viewbaseset_or");
+ GET_MOD_ATTR(viewbaseset_sub_func, "_viewbaseset_sub");
+ GET_MOD_ATTR(viewbaseset_xor_func, "_viewbaseset_xor");
+
+ GET_MOD_ATTR(abc_itemsview_register_func, "_abc_itemsview_register");
+ GET_MOD_ATTR(abc_keysview_register_func, "_abc_keysview_register");
+ GET_MOD_ATTR(abc_valuesview_register_func, "_abc_valuesview_register");
+
+ GET_MOD_ATTR(itemsview_repr_func, "_itemsview_isdisjoint");
+ GET_MOD_ATTR(itemsview_repr_func, "_itemsview_repr");
+
+ GET_MOD_ATTR(keysview_repr_func, "_keysview_repr");
+ GET_MOD_ATTR(keysview_isdisjoint_func, "_keysview_isdisjoint");
+
+ GET_MOD_ATTR(valuesview_repr_func, "_valuesview_repr");
+
+ if (PyType_Ready(&multidict_itemsview_type) < 0 ||
+ PyType_Ready(&multidict_valuesview_type) < 0 ||
+ PyType_Ready(&multidict_keysview_type) < 0)
+ {
+ goto fail;
+ }
+
+ // abc.ItemsView.register(_ItemsView)
+ reg_func_call_result = PyObject_CallFunctionObjArgs(
+ abc_itemsview_register_func, (PyObject*)&multidict_itemsview_type, NULL);
+ if (reg_func_call_result == NULL) {
+ goto fail;
+ }
+ Py_DECREF(reg_func_call_result);
+
+ // abc.KeysView.register(_KeysView)
+ reg_func_call_result = PyObject_CallFunctionObjArgs(
+ abc_keysview_register_func, (PyObject*)&multidict_keysview_type, NULL);
+ if (reg_func_call_result == NULL) {
+ goto fail;
+ }
+ Py_DECREF(reg_func_call_result);
+
+ // abc.ValuesView.register(_KeysView)
+ reg_func_call_result = PyObject_CallFunctionObjArgs(
+ abc_valuesview_register_func, (PyObject*)&multidict_valuesview_type, NULL);
+ if (reg_func_call_result == NULL) {
+ goto fail;
+ }
+ Py_DECREF(reg_func_call_result);
+
+ Py_DECREF(module);
+ return 0;
+
+fail:
+ Py_CLEAR(module);
+ return -1;
+
+#undef GET_MOD_ATTR
+}
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/third_party/python/multidict/multidict/py.typed b/third_party/python/multidict/multidict/py.typed
new file mode 100644
index 0000000000..dfe8cc048e
--- /dev/null
+++ b/third_party/python/multidict/multidict/py.typed
@@ -0,0 +1 @@
+PEP-561 marker. \ No newline at end of file
diff --git a/third_party/python/multidict/pyproject.toml b/third_party/python/multidict/pyproject.toml
new file mode 100644
index 0000000000..f1b83b8f62
--- /dev/null
+++ b/third_party/python/multidict/pyproject.toml
@@ -0,0 +1,11 @@
+[build-system]
+requires = ["setuptools>=40", "wheel"]
+
+
+[tool.towncrier]
+package = "multidict"
+filename = "CHANGES.rst"
+directory = "CHANGES/"
+title_format = "{version} ({project_date})"
+template = "CHANGES/.TEMPLATE.rst"
+issue_format = "`#{issue} <https://github.com/aio-libs/multidict/issues/{issue}>`_"
diff --git a/third_party/python/multidict/setup.cfg b/third_party/python/multidict/setup.cfg
new file mode 100644
index 0000000000..2c11fd4aed
--- /dev/null
+++ b/third_party/python/multidict/setup.cfg
@@ -0,0 +1,37 @@
+[aliases]
+test = pytest
+
+[metadata]
+license_files =
+ LICENSE
+long_description = file: README.rst
+
+[flake8]
+ignore = E302,E701,E305,E704,F811,N811, W503
+max-line-length = 88
+
+[isort]
+multi_line_output = 3
+include_trailing_comma = True
+force_grid_wrap = 0
+use_parentheses = True
+known_first_party = multidict
+known_third_party = pytest
+
+[tool:pytest]
+testpaths = tests
+norecursedirs = dist build .tox docs requirements tools
+addopts = --doctest-modules --cov=multidict --cov-report term-missing:skip-covered --cov-report xml --junitxml=junit-test-results.xml -v
+doctest_optionflags = ALLOW_UNICODE ELLIPSIS
+junit_family = xunit2
+
+[mypy-pytest]
+ignore_missing_imports = true
+
+[mypy-multidict._multidict]
+ignore_missing_imports = true
+
+[egg_info]
+tag_build =
+tag_date = 0
+
diff --git a/third_party/python/multidict/setup.py b/third_party/python/multidict/setup.py
new file mode 100644
index 0000000000..044f1d72ed
--- /dev/null
+++ b/third_party/python/multidict/setup.py
@@ -0,0 +1,96 @@
+import codecs
+import os
+import platform
+import re
+import sys
+
+from setuptools import Extension, setup
+
+NO_EXTENSIONS = bool(os.environ.get("MULTIDICT_NO_EXTENSIONS"))
+
+if sys.implementation.name != "cpython":
+ NO_EXTENSIONS = True
+
+CFLAGS = ["-O2"]
+# CFLAGS = ['-g']
+if platform.system() != "Windows":
+ CFLAGS.extend(
+ [
+ "-std=c99",
+ "-Wall",
+ "-Wsign-compare",
+ "-Wconversion",
+ "-fno-strict-aliasing",
+ "-pedantic",
+ ]
+ )
+
+extensions = [
+ Extension(
+ "multidict._multidict",
+ ["multidict/_multidict.c"],
+ extra_compile_args=CFLAGS,
+ ),
+]
+
+
+with codecs.open(
+ os.path.join(
+ os.path.abspath(os.path.dirname(__file__)), "multidict", "__init__.py"
+ ),
+ "r",
+ "latin1",
+) as fp:
+ try:
+ version = re.findall(r'^__version__ = "([^"]+)"\r?$', fp.read(), re.M)[0]
+ except IndexError:
+ raise RuntimeError("Unable to determine version.")
+
+
+def read(f):
+ return open(os.path.join(os.path.dirname(__file__), f)).read().strip()
+
+
+args = dict(
+ name="multidict",
+ version=version,
+ description=("multidict implementation"),
+ long_description=read("README.rst"),
+ classifiers=[
+ "License :: OSI Approved :: Apache Software License",
+ "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 :: 3.9",
+ "Development Status :: 5 - Production/Stable",
+ ],
+ author="Andrew Svetlov",
+ author_email="andrew.svetlov@gmail.com",
+ url="https://github.com/aio-libs/multidict",
+ project_urls={
+ "Chat: Gitter": "https://gitter.im/aio-libs/Lobby",
+ "CI: Azure Pipelines": "https://dev.azure.com/aio-libs/multidict/_build",
+ "Coverage: codecov": "https://codecov.io/github/aio-libs/multidict",
+ "Docs: RTD": "https://multidict.readthedocs.io",
+ "GitHub: issues": "https://github.com/aio-libs/multidict/issues",
+ "GitHub: repo": "https://github.com/aio-libs/multidict",
+ },
+ license="Apache 2",
+ packages=["multidict"],
+ python_requires=">=3.6",
+ include_package_data=True,
+)
+
+if not NO_EXTENSIONS:
+ print("*********************")
+ print("* Accelerated build *")
+ print("*********************")
+ setup(ext_modules=extensions, **args)
+else:
+ print("*********************")
+ print("* Pure Python build *")
+ print("*********************")
+ setup(**args)