diff options
Diffstat (limited to 'third_party/python/multidict')
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) |