summaryrefslogtreecommitdiffstats
path: root/contrib/python
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2025-04-15 10:46:38 +0300
committerrobot-piglet <[email protected]>2025-04-15 11:01:32 +0300
commit5cc548515eb7cc949d3a9b9f9e227ca548a0d96f (patch)
tree0cf61d14efda39e876aec5c0061c059dfc52057c /contrib/python
parentfb21adf828e369616d8245611ae963231eaadb62 (diff)
Intermediate changes
commit_hash:fc4934b5fdd81814af456297e079f0772040e2d6
Diffstat (limited to 'contrib/python')
-rw-r--r--contrib/python/multidict/.dist-info/METADATA13
-rw-r--r--contrib/python/multidict/README.rst8
-rw-r--r--contrib/python/multidict/multidict/__init__.py13
-rw-r--r--contrib/python/multidict/multidict/_compat.py3
-rw-r--r--contrib/python/multidict/multidict/_multidict.c1216
-rw-r--r--contrib/python/multidict/multidict/_multidict_base.py176
-rw-r--r--contrib/python/multidict/multidict/_multidict_py.py429
-rw-r--r--contrib/python/multidict/multidict/_multilib/defs.h1
-rw-r--r--contrib/python/multidict/multidict/_multilib/dict.h1
-rw-r--r--contrib/python/multidict/multidict/_multilib/istr.h94
-rw-r--r--contrib/python/multidict/multidict/_multilib/iter.h38
-rw-r--r--contrib/python/multidict/multidict/_multilib/pair_list.h1134
-rw-r--r--contrib/python/multidict/multidict/_multilib/parser.h147
-rw-r--r--contrib/python/multidict/multidict/_multilib/pythoncapi_compat.h1121
-rw-r--r--contrib/python/multidict/multidict/_multilib/views.h1477
-rw-r--r--contrib/python/multidict/tests/gen_pickles.py10
-rw-r--r--contrib/python/multidict/tests/istr-c-extension.pickle.08
-rw-r--r--contrib/python/multidict/tests/istr-c-extension.pickle.1bin0 -> 47 bytes
-rw-r--r--contrib/python/multidict/tests/istr-c-extension.pickle.2bin0 -> 48 bytes
-rw-r--r--contrib/python/multidict/tests/istr-c-extension.pickle.3bin0 -> 48 bytes
-rw-r--r--contrib/python/multidict/tests/istr-c-extension.pickle.4bin0 -> 54 bytes
-rw-r--r--contrib/python/multidict/tests/istr-c-extension.pickle.5bin0 -> 54 bytes
-rw-r--r--contrib/python/multidict/tests/istr-pure-python.pickle.014
-rw-r--r--contrib/python/multidict/tests/istr-pure-python.pickle.1bin0 -> 100 bytes
-rw-r--r--contrib/python/multidict/tests/istr-pure-python.pickle.2bin0 -> 51 bytes
-rw-r--r--contrib/python/multidict/tests/istr-pure-python.pickle.3bin0 -> 51 bytes
-rw-r--r--contrib/python/multidict/tests/istr-pure-python.pickle.4bin0 -> 57 bytes
-rw-r--r--contrib/python/multidict/tests/istr-pure-python.pickle.5bin0 -> 57 bytes
-rw-r--r--contrib/python/multidict/tests/test_circular_imports.py3
-rw-r--r--contrib/python/multidict/tests/test_incorrect_args.py126
-rw-r--r--contrib/python/multidict/tests/test_istr.py7
-rw-r--r--contrib/python/multidict/tests/test_multidict.py378
-rw-r--r--contrib/python/multidict/tests/test_multidict_benchmarks.py505
-rw-r--r--contrib/python/multidict/tests/test_mutable_multidict.py65
-rw-r--r--contrib/python/multidict/tests/test_pickle.py33
-rw-r--r--contrib/python/multidict/tests/test_views_benchmarks.py229
-rw-r--r--contrib/python/multidict/ya.make3
37 files changed, 5276 insertions, 1976 deletions
diff --git a/contrib/python/multidict/.dist-info/METADATA b/contrib/python/multidict/.dist-info/METADATA
index b5c6dad90ba..3cd254428e2 100644
--- a/contrib/python/multidict/.dist-info/METADATA
+++ b/contrib/python/multidict/.dist-info/METADATA
@@ -1,6 +1,6 @@
-Metadata-Version: 2.2
+Metadata-Version: 2.4
Name: multidict
-Version: 6.2.0
+Version: 6.3.0
Summary: multidict implementation
Home-page: https://github.com/aio-libs/multidict
Author: Andrew Svetlov
@@ -29,6 +29,7 @@ Requires-Python: >=3.9
Description-Content-Type: text/x-rst
License-File: LICENSE
Requires-Dist: typing-extensions>=4.1.0; python_version < "3.11"
+Dynamic: license-file
=========
multidict
@@ -38,8 +39,8 @@ multidict
:target: https://github.com/aio-libs/multidict/actions
: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
+.. image:: https://codecov.io/gh/aio-libs/multidict/branch/master/graph/badge.svg?flag=pytest
+ :target: https://codecov.io/gh/aio-libs/multidict?flags[]=pytest
:alt: Coverage metrics
.. image:: https://img.shields.io/pypi/v/multidict.svg
@@ -50,6 +51,10 @@ multidict
:target: https://multidict.aio-libs.org
:alt: Read The Docs build status badge
+.. image:: https://img.shields.io/endpoint?url=https://codspeed.io/badge.json
+ :target: https://codspeed.io/aio-libs/multidict
+ :alt: CodSpeed
+
.. image:: https://img.shields.io/pypi/pyversions/multidict.svg
:target: https://pypi.org/project/multidict
:alt: Python versions
diff --git a/contrib/python/multidict/README.rst b/contrib/python/multidict/README.rst
index 40d84b85851..fbab818a979 100644
--- a/contrib/python/multidict/README.rst
+++ b/contrib/python/multidict/README.rst
@@ -6,8 +6,8 @@ multidict
:target: https://github.com/aio-libs/multidict/actions
: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
+.. image:: https://codecov.io/gh/aio-libs/multidict/branch/master/graph/badge.svg?flag=pytest
+ :target: https://codecov.io/gh/aio-libs/multidict?flags[]=pytest
:alt: Coverage metrics
.. image:: https://img.shields.io/pypi/v/multidict.svg
@@ -18,6 +18,10 @@ multidict
:target: https://multidict.aio-libs.org
:alt: Read The Docs build status badge
+.. image:: https://img.shields.io/endpoint?url=https://codspeed.io/badge.json
+ :target: https://codspeed.io/aio-libs/multidict
+ :alt: CodSpeed
+
.. image:: https://img.shields.io/pypi/pyversions/multidict.svg
:target: https://pypi.org/project/multidict
:alt: Python versions
diff --git a/contrib/python/multidict/multidict/__init__.py b/contrib/python/multidict/multidict/__init__.py
index b6b532a1f25..7159a2d6c0f 100644
--- a/contrib/python/multidict/multidict/__init__.py
+++ b/contrib/python/multidict/multidict/__init__.py
@@ -22,7 +22,7 @@ __all__ = (
"getversion",
)
-__version__ = "6.2.0"
+__version__ = "6.3.0"
if TYPE_CHECKING or not USE_EXTENSIONS:
@@ -35,14 +35,25 @@ if TYPE_CHECKING or not USE_EXTENSIONS:
istr,
)
else:
+ from collections.abc import ItemsView, KeysView, ValuesView
+
from ._multidict import (
CIMultiDict,
CIMultiDictProxy,
MultiDict,
MultiDictProxy,
+ _ItemsView,
+ _KeysView,
+ _ValuesView,
getversion,
istr,
)
+ MultiMapping.register(MultiDictProxy)
+ MutableMultiMapping.register(MultiDict)
+ KeysView.register(_KeysView)
+ ItemsView.register(_ItemsView)
+ ValuesView.register(_ValuesView)
+
upstr = istr
diff --git a/contrib/python/multidict/multidict/_compat.py b/contrib/python/multidict/multidict/_compat.py
index 4713da2ceac..264d327e6a9 100644
--- a/contrib/python/multidict/multidict/_compat.py
+++ b/contrib/python/multidict/multidict/_compat.py
@@ -10,5 +10,6 @@ USE_EXTENSIONS = not NO_EXTENSIONS and not PYPY
if USE_EXTENSIONS:
try:
from . import _multidict # type: ignore[attr-defined] # noqa: F401
- except ImportError:
+ except ImportError: # pragma: no cover
+ # FIXME: Refactor for coverage. See #837.
USE_EXTENSIONS = False
diff --git a/contrib/python/multidict/multidict/_multidict.c b/contrib/python/multidict/multidict/_multidict.c
index ebb1949f0a7..64d3c395e96 100644
--- a/contrib/python/multidict/multidict/_multidict.c
+++ b/contrib/python/multidict/multidict/_multidict.c
@@ -9,19 +9,9 @@
#include "_multilib/pair_list.h"
#include "_multilib/dict.h"
#include "_multilib/iter.h"
+#include "_multilib/parser.h"
#include "_multilib/views.h"
-#if PY_MINOR_VERSION < 12
-#ifndef _PyArg_UnpackKeywords
-#define FASTCALL_OLD
-#endif
-#endif
-
-
-static PyObject *collections_abc_mapping;
-static PyObject *collections_abc_mut_mapping;
-static PyObject *collections_abc_mut_multi_mapping;
-static PyObject *repr_func;
static PyTypeObject multidict_type;
static PyTypeObject cimultidict_type;
@@ -48,398 +38,180 @@ 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 *val = NULL;
- 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;
+ if (pair_list_get_one(&self->pairs, key, &val) <0) {
+ return NULL;
}
- 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;
+ if (val == NULL) {
+ if (_default != NULL) {
+ Py_INCREF(_default);
+ return _default;
+ } else {
+ PyErr_SetObject(PyExc_KeyError, key);
+ return NULL;
}
+ } else {
+ return val;
}
-
- 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)
+_multidict_extend(MultiDictObject *self, PyObject *arg,
+ PyObject *kwds, const char *name, int do_add)
{
- PyObject *key = NULL,
- *value = NULL;
+ PyObject *used = NULL;
+ PyObject *seq = NULL;
+ pair_list_t *list;
- 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;
+ if (!do_add) {
+ used = PyDict_New();
+ if (used == NULL) {
+ goto fail;
}
}
- 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;
+ if (kwds && !PyArg_ValidateKeywordArguments(kwds)) {
+ goto fail;
}
- while ((item = PyIter_Next(iter)) != NULL) {
- if (PyTuple_CheckExact(item)) {
- if (PyTuple_GET_SIZE(item) != 2) {
- goto invalid_type;
+ if (arg != NULL) {
+ if (MultiDict_CheckExact(arg) || CIMultiDict_CheckExact(arg)) {
+ list = &((MultiDictObject*)arg)->pairs;
+ if (pair_list_update_from_pair_list(&self->pairs, used, list) < 0) {
+ goto fail;
}
- 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_Size(item) != 2) {
- goto invalid_type;
+ } else if (MultiDictProxy_CheckExact(arg) || CIMultiDictProxy_CheckExact(arg)) {
+ list = &((MultiDictProxyObject*)arg)->md->pairs;
+ if (pair_list_update_from_pair_list(&self->pairs, used, list) < 0) {
+ goto fail;
}
- key = PyList_GetItemRef(item, 0);
- if (key == NULL) {
- goto invalid_type;
+ } else if (PyDict_CheckExact(arg)) {
+ if (pair_list_update_from_dict(&self->pairs, used, arg) < 0) {
+ goto fail;
}
- value = PyList_GetItemRef(item, 1);
- if (value == NULL) {
- goto invalid_type;
+ } else {
+ seq = PyMapping_Items(arg);
+ if (seq == NULL) {
+ PyErr_Clear();
+ seq = Py_NewRef(arg);
}
- }
- else if (PySequence_Check(item)) {
- if (PySequence_Size(item) != 2) {
- goto invalid_type;
+
+ if (pair_list_update_from_seq(&self->pairs, used, seq) < 0) {
+ goto fail;
}
- key = PySequence_GetItem(item, 0);
- value = PySequence_GetItem(item, 1);
- } else {
- goto invalid_type;
}
+ }
- if (pair_list_add(&self->pairs, key, value) < 0) {
+ if (kwds != NULL) {
+ if (pair_list_update_from_dict(&self->pairs, used, kwds) < 0) {
goto fail;
}
- Py_CLEAR(key);
- Py_CLEAR(value);
- Py_CLEAR(item);
}
- Py_DECREF(iter);
-
- if (PyErr_Occurred()) {
- return -1;
+ if (!do_add) {
+ if (pair_list_post_update(&self->pairs, used) < 0) {
+ goto fail;
+ }
}
-
+ Py_CLEAR(seq);
+ Py_CLEAR(used);
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);
+ Py_CLEAR(seq);
+ Py_CLEAR(used);
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);
+static inline Py_ssize_t
+_multidict_extend_parse_args(PyObject *args, PyObject *kwds,
+ const char *name, PyObject **parg)
+{
+ Py_ssize_t size = 0;
+ Py_ssize_t s;
+ if (args) {
+ size = PyTuple_GET_SIZE(args);
+ if (size > 1) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "%s takes from 1 to 2 positional arguments but %zd were given",
+ name, size + 1, NULL
+ );
+ *parg = NULL;
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);
+ if (size == 1) {
+ *parg = Py_NewRef(PyTuple_GET_ITEM(args, 0));
+ s = PyObject_Length(*parg);
+ if (s < 0) {
+ // e.g. cannot calc size of generator object
+ PyErr_Clear();
} else {
- arg_items = PyMapping_Items(arg);
- }
- if (arg_items == NULL) {
- return -1;
+ size += s;
}
} else {
- arg_items = arg;
- Py_INCREF(arg_items);
+ *parg = NULL;
}
- 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);
+ if (kwds != NULL) {
+ s = PyDict_Size(kwds);
+ if (s < 0) {
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);
+ size += s;
}
- 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 from 1 to 2 positional arguments but %zd were given",
- name, PyObject_Length(args) + 1, 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;
+ return size;
}
static inline PyObject *
-_multidict_copy(MultiDictObject *self, PyTypeObject *multidict_tp_object)
+multidict_copy(MultiDictObject *self)
{
MultiDictObject *new_multidict = NULL;
- PyObject *arg_items = NULL,
- *items = NULL;
-
new_multidict = (MultiDictObject*)PyType_GenericNew(
- multidict_tp_object, NULL, NULL);
+ Py_TYPE(self), 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) {
+ if (Py_TYPE(self)->tp_init((PyObject*)new_multidict, NULL, NULL) < 0) {
goto fail;
}
- Py_INCREF(items);
- PyTuple_SET_ITEM(arg_items, 0, items);
-
- if (_multidict_extend(
- new_multidict, arg_items, NULL, "copy", 1) < 0)
- {
+ if (pair_list_update_from_pair_list(&new_multidict->pairs,
+ NULL, &self->pairs) < 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);
-
+ Py_CLEAR(new_multidict);
return NULL;
}
static inline PyObject *
_multidict_proxy_copy(MultiDictProxyObject *self, PyTypeObject *type)
{
- PyObject *new_multidict = PyType_GenericNew(type, NULL, NULL);
+ MultiDictObject *new_multidict = NULL;
+ new_multidict = (MultiDictObject*)PyType_GenericNew(type, NULL, NULL);
if (new_multidict == NULL) {
goto fail;
}
- if (type->tp_init(new_multidict, NULL, NULL) < 0) {
+ if (type->tp_init((PyObject*)new_multidict, NULL, NULL) < 0) {
goto fail;
}
- if (_multidict_extend_with_args(
- (MultiDictObject*)new_multidict, (PyObject*)self, NULL, "copy", 1) < 0)
- {
+ if (pair_list_update_from_pair_list(&new_multidict->pairs,
+ NULL, &self->md->pairs) < 0) {
goto fail;
}
-
- return new_multidict;
-
+ return (PyObject*)new_multidict;
fail:
- Py_XDECREF(new_multidict);
+ Py_CLEAR(new_multidict);
return NULL;
}
@@ -449,173 +221,74 @@ fail:
static inline PyObject *
multidict_getall(
MultiDictObject *self,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- PyObject *args,
- PyObject *kwds
-#else
PyObject *const *args,
Py_ssize_t nargs,
PyObject *kwnames
-#endif
)
{
PyObject *list = NULL,
*key = NULL,
*_default = NULL;
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- static char *getall_keywords[] = {"key", "default", NULL};
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:getall",
- getall_keywords, &key, &_default))
- {
+ if (parse2("getall", args, nargs, kwnames, 1,
+ "key", &key, "default", &_default) < 0) {
return NULL;
}
-#else
- static const char * const _keywords[] = {"key", "default", NULL};
-#ifdef FASTCALL_OLD
- static _PyArg_Parser _parser = {"O|O:getall", _keywords, 0};
- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
- &key, &_default)) {
+ if (pair_list_get_all(&self->pairs, key, &list) <0) {
return NULL;
}
-#else
- static _PyArg_Parser _parser = {NULL, _keywords, "getall", 0};
- PyObject *argsbuf[2];
- Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
- args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames,
- &_parser, 1, 2, 0, argsbuf);
- if (!args) {
- return NULL;
- }
- key = args[0];
- if (!noptargs) {
- goto skip_optional_pos;
- }
- _default = args[1];
-skip_optional_pos:
-#endif
-#endif
- list = pair_list_get_all(&self->pairs, key);
-
- if (list == NULL &&
- PyErr_ExceptionMatches(PyExc_KeyError) &&
- _default != NULL)
- {
- PyErr_Clear();
- Py_INCREF(_default);
- return _default;
+ if (list == NULL) {
+ if (_default != NULL) {
+ Py_INCREF(_default);
+ return _default;
+ } else {
+ PyErr_SetObject(PyExc_KeyError, key);
+ return NULL;
+ }
+ } else {
+ return list;
}
-
- return list;
}
static inline PyObject *
multidict_getone(
MultiDictObject *self,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- PyObject *args,
- PyObject *kwds
-#else
PyObject *const *args,
Py_ssize_t nargs,
PyObject *kwnames
-#endif
)
{
PyObject *key = NULL,
*_default = NULL;
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- static char *getone_keywords[] = {"key", "default", NULL};
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:getone",
- getone_keywords, &key, &_default))
- {
+ if (parse2("getone", args, nargs, kwnames, 1,
+ "key", &key, "default", &_default) < 0) {
return NULL;
}
-
-#else
- static const char * const _keywords[] = {"key", "default", NULL};
-#ifdef FASTCALL_OLD
- static _PyArg_Parser _parser = {"O|O:getone", _keywords, 0};
- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
- &key, &_default)) {
- return NULL;
- }
-#else
- static _PyArg_Parser _parser = {NULL, _keywords, "getone", 0};
- PyObject *argsbuf[2];
- Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
- args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames,
- &_parser, 1, 2, 0, argsbuf);
- if (!args) {
- return NULL;
- }
- key = args[0];
- if (!noptargs) {
- goto skip_optional_pos;
- }
-
- _default = args[1];
-skip_optional_pos:
-#endif
-#endif
return _multidict_getone(self, key, _default);
}
static inline PyObject *
multidict_get(
MultiDictObject *self,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- PyObject *args,
- PyObject *kwds
-#else
PyObject *const *args,
Py_ssize_t nargs,
PyObject *kwnames
-#endif
)
{
PyObject *key = NULL,
- *_default = Py_None,
+ *_default = NULL,
*ret;
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- static char *getone_keywords[] = {"key", "default", NULL};
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:getone",
- getone_keywords, &key, &_default))
- {
+ if (parse2("get", args, nargs, kwnames, 1,
+ "key", &key, "default", &_default) < 0) {
return NULL;
}
-#else
- static const char * const _keywords[] = {"key", "default", NULL};
-#ifdef FASTCALL_OLD
- static _PyArg_Parser _parser = {"O|O:get", _keywords, 0};
- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
- &key, &_default)) {
- return NULL;
+ if (_default == NULL) {
+ // fixme, _default is potentially dangerous borrowed ref here
+ _default = Py_None;
}
-#else
- static _PyArg_Parser _parser = {NULL, _keywords, "get", 0};
- PyObject *argsbuf[2];
- Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
- args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames,
- &_parser, 1, 2, 0, argsbuf);
- if (!args) {
- return NULL;
- }
- key = args[0];
- if (!noptargs) {
- goto skip_optional_pos;
- }
-
- _default = args[1];
-skip_optional_pos:
-#endif
-#endif
ret = _multidict_getone(self, key, _default);
return ret;
}
@@ -623,19 +296,19 @@ skip_optional_pos:
static inline PyObject *
multidict_keys(MultiDictObject *self)
{
- return multidict_keysview_new((PyObject*)self);
+ return multidict_keysview_new(self);
}
static inline PyObject *
multidict_items(MultiDictObject *self)
{
- return multidict_itemsview_new((PyObject*)self);
+ return multidict_itemsview_new(self);
}
static inline PyObject *
multidict_values(MultiDictObject *self)
{
- return multidict_valuesview_new((PyObject*)self);
+ return multidict_valuesview_new(self);
}
static inline PyObject *
@@ -672,10 +345,14 @@ ret:
}
static inline PyObject *
-multidict_repr(PyObject *self)
+multidict_repr(MultiDictObject *self)
{
- return PyObject_CallFunctionObjArgs(
- repr_func, self, NULL);
+ PyObject *name = PyObject_GetAttrString((PyObject*)Py_TYPE(self), "__name__");
+ if (name == NULL)
+ return NULL;
+ PyObject *ret = pair_list_repr(&self->pairs, name, true, true);
+ Py_CLEAR(name);
+ return ret;
}
static inline Py_ssize_t
@@ -703,7 +380,7 @@ multidict_mp_as_subscript(MultiDictObject *self, PyObject *key, PyObject *val)
static inline int
multidict_sq_contains(MultiDictObject *self, PyObject *key)
{
- return pair_list_contains(&self->pairs, key);
+ return pair_list_contains(&self->pairs, key, NULL);
}
static inline PyObject *
@@ -715,59 +392,56 @@ multidict_tp_iter(MultiDictObject *self)
static inline PyObject *
multidict_tp_richcompare(PyObject *self, PyObject *other, int op)
{
- // TODO: refactoring me with love
-
- int cmp = 0;
+ int cmp;
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 (self == other) {
+ cmp = 1;
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 (MultiDict_CheckExact(other) || CIMultiDict_CheckExact(other)) {
+ cmp = pair_list_eq(
+ &((MultiDictObject*)self)->pairs,
+ &((MultiDictObject*)other)->pairs
);
- if (cmp < 0) {
- return NULL;
+ } else if (MultiDictProxy_CheckExact(other) || CIMultiDictProxy_CheckExact(other)) {
+ cmp = pair_list_eq(
+ &((MultiDictObject*)self)->pairs,
+ &((MultiDictProxyObject*)other)->md->pairs
+ );
+ } else {
+ bool fits = false;
+ fits = PyDict_Check(other);
+ if (!fits) {
+ PyObject *keys = PyMapping_Keys(other);
+ if (keys != NULL) {
+ fits = true;
+ } else {
+ // reset AttributeError exception
+ PyErr_Clear();
+ }
+ Py_CLEAR(keys);
}
- if (op == Py_NE) {
- cmp = !cmp;
+ if (fits) {
+ cmp = pair_list_eq_to_mapping(&((MultiDictObject*)self)->pairs, other);
+ } else {
+ cmp = 0; // e.g., multidict is not equal to a list
}
- 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);
+ if (op == Py_NE) {
+ cmp = !cmp;
}
-
- Py_RETURN_NOTIMPLEMENTED;
+ return PyBool_FromLong(cmp);
}
static inline void
@@ -818,10 +492,15 @@ PyDoc_STRVAR(multidict_values_doc,
static inline int
multidict_tp_init(MultiDictObject *self, PyObject *args, PyObject *kwds)
{
- if (pair_list_init(&self->pairs) < 0) {
+ PyObject *arg = NULL;
+ Py_ssize_t size = _multidict_extend_parse_args(args, kwds, "MultiDict", &arg);
+ if (size < 0) {
+ return -1;
+ }
+ if (pair_list_init(&self->pairs, size) < 0) {
return -1;
}
- if (_multidict_extend(self, args, kwds, "MultiDict", 1) < 0) {
+ if (_multidict_extend(self, arg, kwds, "MultiDict", 1) < 0) {
return -1;
}
return 0;
@@ -830,50 +509,18 @@ multidict_tp_init(MultiDictObject *self, PyObject *args, PyObject *kwds)
static inline PyObject *
multidict_add(
MultiDictObject *self,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- PyObject *args,
- PyObject *kwds
-#else
PyObject *const *args,
Py_ssize_t nargs,
PyObject *kwnames
-#endif
)
{
PyObject *key = NULL,
*val = NULL;
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- static char *kwlist[] = {"key", "value", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO:add",
- kwlist, &key, &val))
- {
+ if (parse2("add", args, nargs, kwnames, 2,
+ "key", &key, "value", &val) < 0) {
return NULL;
}
-#else
- static const char * const _keywords[] = {"key", "value", NULL};
-#ifdef FASTCALL_OLD
- static _PyArg_Parser _parser = {"OO:add", _keywords, 0};
- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
- &key, &val)) {
- return NULL;
- }
-#else
- static _PyArg_Parser _parser = {
- .keywords = _keywords,
- .fname = "add",
- .kwtuple = NULL,
- };
- PyObject *argsbuf[2];
- args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames,
- &_parser, 2, 2, 0, argsbuf);
- if (!args) {
- return NULL;
- }
- key = args[0];
- val = args[1];
-#endif
-#endif
if (pair_list_add(&self->pairs, key, val) < 0) {
return NULL;
}
@@ -882,15 +529,15 @@ multidict_add(
}
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) {
+ PyObject *arg = NULL;
+ Py_ssize_t size = _multidict_extend_parse_args(args, kwds, "extend", &arg);
+ if (size < 0) {
+ return NULL;
+ }
+ pair_list_grow(&self->pairs, size);
+ if (_multidict_extend(self, arg, kwds, "extend", 1) < 0) {
return NULL;
}
@@ -910,268 +557,118 @@ multidict_clear(MultiDictObject *self)
static inline PyObject *
multidict_setdefault(
MultiDictObject *self,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- PyObject *args,
- PyObject *kwds
-#else
PyObject *const *args,
Py_ssize_t nargs,
PyObject *kwnames
-#endif
)
{
PyObject *key = NULL,
*_default = NULL;
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- static char *setdefault_keywords[] = {"key", "default", NULL};
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:setdefault",
- setdefault_keywords, &key, &_default))
- {
- return NULL;
- }
-#else
- static const char * const _keywords[] = {"key", "default", NULL};
-#ifdef FASTCALL_OLD
- static _PyArg_Parser _parser = {"O|O:setdefault", _keywords, 0};
- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
- &key, &_default)) {
- return NULL;
- }
-#else
- static _PyArg_Parser _parser = {NULL, _keywords, "setdefault", 0};
- PyObject *argsbuf[3];
- Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
-
- args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames,
- &_parser, 1, 2, 0, argsbuf);
- if (!args) {
+ if (parse2("setdefault", args, nargs, kwnames, 1,
+ "key", &key, "default", &_default) < 0) {
return NULL;
}
- key = args[0];
- if (!noptargs) {
- goto skip_optional_pos;
- }
- _default = args[1];
-
-skip_optional_pos:
-#endif
-#endif
return pair_list_set_default(&self->pairs, key, _default);
}
static inline PyObject *
multidict_popone(
MultiDictObject *self,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- PyObject *args,
- PyObject *kwds
-#else
PyObject *const *args,
Py_ssize_t nargs,
PyObject *kwnames
-#endif
)
{
PyObject *key = NULL,
*_default = NULL,
*ret_val = NULL;
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- static char *popone_keywords[] = {"key", "default", NULL};
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:popone",
- popone_keywords, &key, &_default))
- {
+ if (parse2("popone", args, nargs, kwnames, 1,
+ "key", &key, "default", &_default) < 0) {
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;
-#else
- static const char * const _keywords[] = {"key", "default", NULL};
-#ifdef FASTCALL_OLD
- static _PyArg_Parser _parser = {"O|O:popone", _keywords, 0};
- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
- &key, &_default)) {
+ if (pair_list_pop_one(&self->pairs, key, &ret_val) < 0) {
return NULL;
}
-#else
- static _PyArg_Parser _parser = {NULL, _keywords, "popone", 0};
- PyObject *argsbuf[3];
- Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
-
- args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames,
- &_parser, 1, 2, 0, argsbuf);
- if (!args) {
- return NULL;
- }
- key = args[0];
- if (!noptargs) {
- goto skip_optional_pos;
- }
- _default = args[1];
-
-skip_optional_pos:
-#endif
- 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;
+ if (ret_val == NULL) {
+ if (_default != NULL) {
+ Py_INCREF(_default);
+ return _default;
+ } else {
+ PyErr_SetObject(PyExc_KeyError, key);
+ return NULL;
+ }
+ } else {
+ return ret_val;
}
-
- return ret_val;
-#endif
}
static inline PyObject *
multidict_pop(
MultiDictObject *self,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- PyObject *args,
- PyObject *kwds
-#else
PyObject *const *args,
Py_ssize_t nargs,
PyObject *kwnames
-#endif
)
{
PyObject *key = NULL,
*_default = NULL,
*ret_val = NULL;
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- static char *pop_keywords[] = {"key", "default", NULL};
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:popone",
- pop_keywords, &key, &_default))
- {
+ if (parse2("pop", args, nargs, kwnames, 1,
+ "key", &key, "default", &_default) < 0) {
return NULL;
}
-
-#else
- static const char * const _keywords[] = {"key", "default", NULL};
-#ifdef FASTCALL_OLD
- static _PyArg_Parser _parser = {"O|O:pop", _keywords, 0};
- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
- &key, &_default)) {
+ if (pair_list_pop_one(&self->pairs, key, &ret_val) < 0) {
return NULL;
}
-#else
- static _PyArg_Parser _parser = {NULL, _keywords, "pop", 0};
- PyObject *argsbuf[3];
- Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
- args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames,
- &_parser, 1, 2, 0, argsbuf);
- if (!args) {
- return NULL;
- }
- key = args[0];
- if (!noptargs) {
- goto skip_optional_pos;
- }
- _default = args[1];
-
-skip_optional_pos:
-#endif
-#endif
- 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;
+ if (ret_val == NULL) {
+ if (_default != NULL) {
+ Py_INCREF(_default);
+ return _default;
+ } else {
+ PyErr_SetObject(PyExc_KeyError, key);
+ return NULL;
+ }
+ } else {
+ return ret_val;
}
-
- return ret_val;
}
static inline PyObject *
multidict_popall(
MultiDictObject *self,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- PyObject *args,
- PyObject *kwds
-#else
PyObject *const *args,
Py_ssize_t nargs,
PyObject *kwnames
-#endif
)
{
PyObject *key = NULL,
*_default = NULL,
*ret_val = NULL;
-
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- static char *popall_keywords[] = {"key", "default", NULL};
-
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:popall",
- popall_keywords, &key, &_default))
- {
- return NULL;
- }
-#else
- static const char * const _keywords[] = {"key", "default", NULL};
-#ifdef FASTCALL_OLD
- static _PyArg_Parser _parser = {"O|O:popall", _keywords, 0};
- if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
- &key, &_default)) {
+ if (parse2("popall", args, nargs, kwnames, 1,
+ "key", &key, "default", &_default) < 0) {
return NULL;
}
-#else
- static _PyArg_Parser _parser = {NULL, _keywords, "popall", 0};
- PyObject *argsbuf[3];
- Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
-
- args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames,
- &_parser, 1, 2, 0, argsbuf);
- if (!args) {
+ if (pair_list_pop_all(&self->pairs, key, &ret_val) < 0) {
return NULL;
}
- key = args[0];
- if (!noptargs) {
- goto skip_optional_pos;
- }
- _default = args[1];
-skip_optional_pos:
-#endif
-#endif
- 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;
+ if (ret_val == NULL) {
+ if (_default != NULL) {
+ Py_INCREF(_default);
+ return _default;
+ } else {
+ PyErr_SetObject(PyExc_KeyError, key);
+ return NULL;
+ }
+ } else {
+ return ret_val;
}
-
- return ret_val;
}
static inline PyObject *
@@ -1183,7 +680,11 @@ multidict_popitem(MultiDictObject *self)
static inline PyObject *
multidict_update(MultiDictObject *self, PyObject *args, PyObject *kwds)
{
- if (_multidict_extend(self, args, kwds, "update", 0) < 0) {
+ PyObject *arg = NULL;
+ if (_multidict_extend_parse_args(args, kwds, "update", &arg) < 0) {
+ return NULL;
+ }
+ if (_multidict_extend(self, arg, kwds, "update", 0) < 0) {
return NULL;
}
Py_RETURN_NONE;
@@ -1226,10 +727,6 @@ PyDoc_STRVAR(multidict_popitem_doc,
PyDoc_STRVAR(multidict_update_doc,
"Update the dictionary from *other*, overwriting existing keys.");
-
-#define multidict_class_getitem Py_GenericAlias
-
-
PyDoc_STRVAR(sizeof__doc__,
"D.__sizeof__() -> size of D in memory, in bytes");
@@ -1258,34 +755,19 @@ static PyMethodDef multidict_methods[] = {
{
"getall",
(PyCFunction)multidict_getall,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- METH_VARARGS
-#else
- METH_FASTCALL
-#endif
- | METH_KEYWORDS,
+ METH_FASTCALL | METH_KEYWORDS,
multidict_getall_doc
},
{
"getone",
(PyCFunction)multidict_getone,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- METH_VARARGS
-#else
- METH_FASTCALL
-#endif
- | METH_KEYWORDS,
+ METH_FASTCALL | METH_KEYWORDS,
multidict_getone_doc
},
{
"get",
(PyCFunction)multidict_get,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- METH_VARARGS
-#else
- METH_FASTCALL
-#endif
- | METH_KEYWORDS,
+ METH_FASTCALL | METH_KEYWORDS,
multidict_get_doc
},
{
@@ -1309,12 +791,7 @@ static PyMethodDef multidict_methods[] = {
{
"add",
(PyCFunction)multidict_add,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- METH_VARARGS
-#else
- METH_FASTCALL
-#endif
- | METH_KEYWORDS,
+ METH_FASTCALL | METH_KEYWORDS,
multidict_add_doc
},
{
@@ -1338,45 +815,25 @@ static PyMethodDef multidict_methods[] = {
{
"setdefault",
(PyCFunction)multidict_setdefault,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- METH_VARARGS
-#else
- METH_FASTCALL
-#endif
- | METH_KEYWORDS,
+ METH_FASTCALL | METH_KEYWORDS,
multidict_setdefault_doc
},
{
"popone",
(PyCFunction)multidict_popone,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- METH_VARARGS
-#else
- METH_FASTCALL
-#endif
- | METH_KEYWORDS,
+ METH_FASTCALL | METH_KEYWORDS,
multidict_popone_doc
},
{
"pop",
(PyCFunction)multidict_pop,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- METH_VARARGS
-#else
- METH_FASTCALL
-#endif
- | METH_KEYWORDS,
+ METH_FASTCALL | METH_KEYWORDS,
multidict_pop_doc
},
{
"popall",
(PyCFunction)multidict_popall,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- METH_VARARGS
-#else
- METH_FASTCALL
-#endif
- | METH_KEYWORDS,
+ METH_FASTCALL | METH_KEYWORDS,
multidict_popall_doc
},
{
@@ -1399,7 +856,7 @@ static PyMethodDef multidict_methods[] = {
},
{
"__class_getitem__",
- (PyCFunction)multidict_class_getitem,
+ (PyCFunction)Py_GenericAlias,
METH_O | METH_CLASS,
NULL
},
@@ -1447,36 +904,22 @@ static PyTypeObject multidict_type = {
static inline int
cimultidict_tp_init(MultiDictObject *self, PyObject *args, PyObject *kwds)
{
- if (ci_pair_list_init(&self->pairs) < 0) {
+ PyObject *arg = NULL;
+ Py_ssize_t size = _multidict_extend_parse_args(args, kwds, "CIMultiDict", &arg);
+ if (size < 0) {
return -1;
}
- if (_multidict_extend(self, args, kwds, "CIMultiDict", 1) < 0) {
+
+ if (ci_pair_list_init(&self->pairs, size) < 0) {
+ return -1;
+ }
+
+ if (_multidict_extend(self, arg, 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.");
@@ -1492,7 +935,6 @@ static PyTypeObject cimultidict_type = {
.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,
@@ -1547,73 +989,46 @@ multidict_proxy_tp_init(MultiDictProxyObject *self, PyObject *args,
static inline PyObject *
multidict_proxy_getall(
MultiDictProxyObject *self,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- PyObject *args,
- PyObject *kwds
-#else
PyObject *const *args,
Py_ssize_t nargs,
PyObject *kwnames
-#endif
)
{
return multidict_getall(
self->md,
args,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- kwds
-#else
nargs,
kwnames
-#endif
);
}
static inline PyObject *
multidict_proxy_getone(
MultiDictProxyObject *self,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- PyObject *args,
- PyObject *kwds
-#else
PyObject *const *args,
Py_ssize_t nargs,
PyObject *kwnames
-#endif
)
{
return multidict_getone(
self->md, args,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- kwds
-#else
nargs, kwnames
-#endif
);
}
static inline PyObject *
multidict_proxy_get(
MultiDictProxyObject *self,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- PyObject *args,
- PyObject *kwds
-#else
PyObject *const *args,
Py_ssize_t nargs,
PyObject *kwnames
-#endif
)
{
return multidict_get(
self->md,
args,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- kwds
-#else
nargs,
kwnames
-#endif
);
}
@@ -1709,6 +1124,18 @@ multidict_proxy_tp_clear(MultiDictProxyObject *self)
return 0;
}
+static inline PyObject *
+multidict_proxy_repr(MultiDictProxyObject *self)
+{
+ PyObject *name = PyObject_GetAttrString((PyObject*)Py_TYPE(self), "__name__");
+ if (name == NULL)
+ return NULL;
+ PyObject *ret = pair_list_repr(&self->md->pairs, name, true, true);
+ Py_CLEAR(name);
+ return ret;
+}
+
+
static PySequenceMethods multidict_proxy_sequence = {
.sq_contains = (objobjproc)multidict_proxy_sq_contains,
};
@@ -1722,34 +1149,19 @@ static PyMethodDef multidict_proxy_methods[] = {
{
"getall",
(PyCFunction)multidict_proxy_getall,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- METH_VARARGS
-#else
- METH_FASTCALL
-#endif
- | METH_KEYWORDS,
+ METH_FASTCALL | METH_KEYWORDS,
multidict_getall_doc
},
{
"getone",
(PyCFunction)multidict_proxy_getone,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- METH_VARARGS
-#else
- METH_FASTCALL
-#endif
- | METH_KEYWORDS,
+ METH_FASTCALL | METH_KEYWORDS,
multidict_getone_doc
},
{
"get",
(PyCFunction)multidict_proxy_get,
-#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 12
- METH_VARARGS
-#else
- METH_FASTCALL
-#endif
- | METH_KEYWORDS,
+ METH_FASTCALL | METH_KEYWORDS,
multidict_get_doc
},
{
@@ -1784,7 +1196,7 @@ static PyMethodDef multidict_proxy_methods[] = {
},
{
"__class_getitem__",
- (PyCFunction)multidict_class_getitem,
+ (PyCFunction)Py_GenericAlias,
METH_O | METH_CLASS,
NULL
},
@@ -1804,7 +1216,7 @@ static PyTypeObject multidict_proxy_type = {
"multidict._multidict.MultiDictProxy", /* tp_name */
sizeof(MultiDictProxyObject), /* tp_basicsize */
.tp_dealloc = (destructor)multidict_proxy_tp_dealloc,
- .tp_repr = (reprfunc)multidict_repr,
+ .tp_repr = (reprfunc)multidict_proxy_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,
@@ -1930,21 +1342,12 @@ static inline void
module_free(void *m)
{
Py_CLEAR(multidict_str_lower);
- Py_CLEAR(collections_abc_mapping);
- Py_CLEAR(collections_abc_mut_mapping);
- Py_CLEAR(collections_abc_mut_multi_mapping);
+ Py_CLEAR(multidict_str_canonical);
}
static PyMethodDef multidict_module_methods[] = {
- {
- "getversion",
- (PyCFunction)getversion,
- METH_O
- },
- {
- NULL,
- NULL
- } /* sentinel */
+ {"getversion", (PyCFunction)getversion, METH_O},
+ {NULL, NULL} /* sentinel */
};
static PyModuleDef multidict_module = {
@@ -1962,9 +1365,12 @@ PyInit__multidict(void)
if (multidict_str_lower == NULL) {
goto fail;
}
+ multidict_str_canonical = PyUnicode_InternFromString("_canonical");
+ if (multidict_str_canonical == NULL) {
+ goto fail;
+ }
- PyObject *module = NULL,
- *reg_func_call_result = NULL;
+ PyObject *module = NULL;
if (multidict_views_init() < 0) {
goto fail;
@@ -1986,72 +1392,6 @@ PyInit__multidict(void)
goto fail;
}
-#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; \
- }
-
- WITH_MOD("collections.abc");
- GET_MOD_ATTR(collections_abc_mapping, "Mapping");
-
- WITH_MOD("multidict._abc");
- GET_MOD_ATTR(collections_abc_mut_mapping, "MultiMapping");
- GET_MOD_ATTR(collections_abc_mut_multi_mapping, "MutableMultiMapping");
-
- WITH_MOD("multidict._multidict_base");
- GET_MOD_ATTR(repr_func, "_mdrepr");
-
- Py_CLEAR(module); \
-
- /* 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);
if (module == NULL) {
@@ -2097,16 +1437,32 @@ PyInit__multidict(void)
goto fail;
}
+ Py_INCREF(&multidict_keysview_type);
+ if (PyModule_AddObject(
+ module, "_KeysView", (PyObject*)&multidict_keysview_type) < 0)
+ {
+ goto fail;
+ }
+
+ Py_INCREF(&multidict_itemsview_type);
+ if (PyModule_AddObject(
+ module, "_ItemsView", (PyObject*)&multidict_itemsview_type) < 0)
+ {
+ goto fail;
+ }
+
+ Py_INCREF(&multidict_valuesview_type);
+ if (PyModule_AddObject(
+ module, "_ValuesView", (PyObject*)&multidict_valuesview_type) < 0)
+ {
+ goto fail;
+ }
+
return module;
fail:
Py_XDECREF(multidict_str_lower);
- Py_XDECREF(collections_abc_mapping);
- Py_XDECREF(collections_abc_mut_mapping);
- Py_XDECREF(collections_abc_mut_multi_mapping);
+ Py_XDECREF(multidict_str_canonical);
return NULL;
-
-#undef WITH_MOD
-#undef GET_MOD_ATTR
}
diff --git a/contrib/python/multidict/multidict/_multidict_base.py b/contrib/python/multidict/multidict/_multidict_base.py
deleted file mode 100644
index df0d70097a1..00000000000
--- a/contrib/python/multidict/multidict/_multidict_base.py
+++ /dev/null
@@ -1,176 +0,0 @@
-import sys
-from collections.abc import (
- Container,
- ItemsView,
- Iterable,
- KeysView,
- Mapping,
- Set,
- ValuesView,
-)
-from typing import Literal, Union
-
-if sys.version_info >= (3, 10):
- from types import NotImplementedType
-else:
- from typing import Any as NotImplementedType
-
-if sys.version_info >= (3, 11):
- from typing import assert_never
-else:
- from typing_extensions import assert_never
-
-
-def _abc_itemsview_register(view_cls: type[object]) -> None:
- ItemsView.register(view_cls)
-
-
-def _abc_keysview_register(view_cls: type[object]) -> None:
- KeysView.register(view_cls)
-
-
-def _abc_valuesview_register(view_cls: type[object]) -> None:
- ValuesView.register(view_cls)
-
-
-def _viewbaseset_richcmp(
- view: set[object], other: object, op: Literal[0, 1, 2, 3, 4, 5]
-) -> Union[bool, NotImplementedType]:
- if op == 0: # <
- if not isinstance(other, Set):
- return NotImplemented # type: ignore[no-any-return]
- return len(view) < len(other) and view <= other
- elif op == 1: # <=
- if not isinstance(other, Set):
- return NotImplemented # type: ignore[no-any-return]
- 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 # type: ignore[no-any-return]
- 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 # type: ignore[no-any-return]
- return len(view) > len(other) and view >= other
- elif op == 5: # >=
- if not isinstance(other, Set):
- return NotImplemented # type: ignore[no-any-return]
- if len(view) < len(other):
- return False
- for elem in other:
- if elem not in view:
- return False
- return True
- else: # pragma: no cover
- assert_never(op)
-
-
-def _viewbaseset_and(
- view: set[object], other: object
-) -> Union[set[object], NotImplementedType]:
- if not isinstance(other, Iterable):
- return NotImplemented # type: ignore[no-any-return]
- 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: set[object], other: object
-) -> Union[set[object], NotImplementedType]:
- if not isinstance(other, Iterable):
- return NotImplemented # type: ignore[no-any-return]
- 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: set[object], other: object
-) -> Union[set[object], NotImplementedType]:
- if not isinstance(other, Iterable):
- return NotImplemented # type: ignore[no-any-return]
- 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: set[object], other: object
-) -> Union[set[object], NotImplementedType]:
- if not isinstance(other, Iterable):
- return NotImplemented # type: ignore[no-any-return]
- 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: Container[object], other: Iterable[object]) -> bool:
- "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: Iterable[tuple[object, object]]) -> str:
- 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: Container[object], other: Iterable[object]) -> bool:
- "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: Iterable[object]) -> str:
- lst = []
- for k in view:
- lst.append("{!r}".format(k))
- body = ", ".join(lst)
- return "{}({})".format(view.__class__.__name__, body)
-
-
-def _valuesview_repr(view: Iterable[object]) -> str:
- lst = []
- for v in view:
- lst.append("{!r}".format(v))
- body = ", ".join(lst)
- return "{}({})".format(view.__class__.__name__, body)
-
-
-def _mdrepr(md: Mapping[object, object]) -> str:
- 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/contrib/python/multidict/multidict/_multidict_py.py b/contrib/python/multidict/multidict/_multidict_py.py
index b8ecb8b9623..7babdd5e6ef 100644
--- a/contrib/python/multidict/multidict/_multidict_py.py
+++ b/contrib/python/multidict/multidict/_multidict_py.py
@@ -1,5 +1,6 @@
import enum
import sys
+from abc import abstractmethod
from array import array
from collections.abc import (
Callable,
@@ -12,8 +13,10 @@ from collections.abc import (
)
from typing import (
TYPE_CHECKING,
+ Any,
Generic,
NoReturn,
+ Optional,
TypeVar,
Union,
cast,
@@ -32,6 +35,7 @@ class istr(str):
"""Case insensitive str."""
__is_istr__ = True
+ __istr_title__: Optional[str] = None
_V = TypeVar("_V")
@@ -80,8 +84,15 @@ class _Iter(Generic[_T]):
class _ViewBase(Generic[_V]):
- def __init__(self, impl: _Impl[_V]):
+ def __init__(
+ self,
+ impl: _Impl[_V],
+ identfunc: Callable[[str], str],
+ keyfunc: Callable[[str], str],
+ ):
self._impl = impl
+ self._identfunc = identfunc
+ self._keyfunc = keyfunc
def __len__(self) -> int:
return len(self._impl._items)
@@ -91,8 +102,13 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]):
def __contains__(self, item: object) -> bool:
if not isinstance(item, (tuple, list)) or len(item) != 2:
return False
+ key, value = item
+ try:
+ ident = self._identfunc(key)
+ except TypeError:
+ return False
for i, k, v in self._impl._items:
- if item[0] == k and item[1] == v:
+ if ident == i and value == v:
return True
return False
@@ -103,20 +119,163 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]):
for i, k, v in self._impl._items:
if version != self._impl._version:
raise RuntimeError("Dictionary changed during iteration")
- yield k, v
+ yield self._keyfunc(k), v
def __repr__(self) -> str:
lst = []
- for item in self._impl._items:
- lst.append("{!r}: {!r}".format(item[1], item[2]))
+ for i, k, v in self._impl._items:
+ lst.append(f"'{k}': {v!r}")
body = ", ".join(lst)
- return "{}({})".format(self.__class__.__name__, body)
+ return f"<{self.__class__.__name__}({body})>"
+
+ def _parse_item(
+ self, arg: Union[tuple[str, _V], _T]
+ ) -> Optional[tuple[str, str, _V]]:
+ if not isinstance(arg, tuple):
+ return None
+ if len(arg) != 2:
+ return None
+ try:
+ return (self._identfunc(arg[0]), arg[0], arg[1])
+ except TypeError:
+ return None
+
+ def _tmp_set(self, it: Iterable[_T]) -> set[tuple[str, _V]]:
+ tmp = set()
+ for arg in it:
+ item = self._parse_item(arg)
+ if item is None:
+ continue
+ else:
+ tmp.add((item[0], item[2]))
+ return tmp
+
+ def __and__(self, other: Iterable[Any]) -> set[tuple[str, _V]]:
+ ret = set()
+ try:
+ it = iter(other)
+ except TypeError:
+ return NotImplemented
+ for arg in it:
+ item = self._parse_item(arg)
+ if item is None:
+ continue
+ identity, key, value = item
+ for i, k, v in self._impl._items:
+ if i == identity and v == value:
+ ret.add((k, v))
+ return ret
+
+ def __rand__(self, other: Iterable[_T]) -> set[_T]:
+ ret = set()
+ try:
+ it = iter(other)
+ except TypeError:
+ return NotImplemented
+ for arg in it:
+ item = self._parse_item(arg)
+ if item is None:
+ continue
+ identity, key, value = item
+ for i, k, v in self._impl._items:
+ if i == identity and v == value:
+ ret.add(arg)
+ break
+ return ret
+
+ def __or__(self, other: Iterable[_T]) -> set[Union[tuple[str, _V], _T]]:
+ ret: set[Union[tuple[str, _V], _T]] = set(self)
+ try:
+ it = iter(other)
+ except TypeError:
+ return NotImplemented
+ for arg in it:
+ item: Optional[tuple[str, str, _V]] = self._parse_item(arg)
+ if item is None:
+ ret.add(arg)
+ continue
+ identity, key, value = item
+ for i, k, v in self._impl._items:
+ if i == identity and v == value:
+ break
+ else:
+ ret.add(arg)
+ return ret
+
+ def __ror__(self, other: Iterable[_T]) -> set[Union[tuple[str, _V], _T]]:
+ try:
+ ret: set[Union[tuple[str, _V], _T]] = set(other)
+ except TypeError:
+ return NotImplemented
+ tmp = self._tmp_set(ret)
+
+ for i, k, v in self._impl._items:
+ if (i, v) not in tmp:
+ ret.add((k, v))
+ return ret
+
+ def __sub__(self, other: Iterable[_T]) -> set[Union[tuple[str, _V], _T]]:
+ ret: set[Union[tuple[str, _V], _T]] = set()
+ try:
+ it = iter(other)
+ except TypeError:
+ return NotImplemented
+ tmp = self._tmp_set(it)
+
+ for i, k, v in self._impl._items:
+ if (i, v) not in tmp:
+ ret.add((k, v))
+
+ return ret
+
+ def __rsub__(self, other: Iterable[_T]) -> set[_T]:
+ ret: set[_T] = set()
+ try:
+ it = iter(other)
+ except TypeError:
+ return NotImplemented
+ for arg in it:
+ item = self._parse_item(arg)
+ if item is None:
+ ret.add(arg)
+ continue
+
+ identity, key, value = item
+ for i, k, v in self._impl._items:
+ if i == identity and v == value:
+ break
+ else:
+ ret.add(arg)
+ return ret
+
+ def __xor__(self, other: Iterable[_T]) -> set[Union[tuple[str, _V], _T]]:
+ try:
+ rgt = set(other)
+ except TypeError:
+ return NotImplemented
+ ret: set[Union[tuple[str, _V], _T]] = self - rgt
+ ret |= (rgt - self)
+ return ret
+
+ __rxor__ = __xor__
+
+ def isdisjoint(self, other: Iterable[tuple[str, _V]]) -> bool:
+ for arg in other:
+ item = self._parse_item(arg)
+ if item is None:
+ continue
+
+ identity, key, value = item
+ for i, k, v in self._impl._items:
+ if i == identity and v == value:
+ return False
+ return True
class _ValuesView(_ViewBase[_V], ValuesView[_V]):
def __contains__(self, value: object) -> bool:
- for item in self._impl._items:
- if item[2] == value:
+ for i, k, v in self._impl._items:
+ if v == value:
return True
return False
@@ -124,23 +283,26 @@ class _ValuesView(_ViewBase[_V], ValuesView[_V]):
return _Iter(len(self), self._iter(self._impl._version))
def _iter(self, version: int) -> Iterator[_V]:
- for item in self._impl._items:
+ for i, k, v in self._impl._items:
if version != self._impl._version:
raise RuntimeError("Dictionary changed during iteration")
- yield item[2]
+ yield v
def __repr__(self) -> str:
lst = []
- for item in self._impl._items:
- lst.append("{!r}".format(item[2]))
+ for i, k, v in self._impl._items:
+ lst.append(repr(v))
body = ", ".join(lst)
- return "{}({})".format(self.__class__.__name__, body)
+ return f"<{self.__class__.__name__}({body})>"
class _KeysView(_ViewBase[_V], KeysView[str]):
def __contains__(self, key: object) -> bool:
- for item in self._impl._items:
- if item[1] == key:
+ if not isinstance(key, str):
+ return False
+ identity = self._identfunc(key)
+ for i, k, v in self._impl._items:
+ if i == identity:
return True
return False
@@ -148,24 +310,176 @@ class _KeysView(_ViewBase[_V], KeysView[str]):
return _Iter(len(self), self._iter(self._impl._version))
def _iter(self, version: int) -> Iterator[str]:
- for item in self._impl._items:
+ for i, k, v in self._impl._items:
if version != self._impl._version:
raise RuntimeError("Dictionary changed during iteration")
- yield item[1]
+ yield self._keyfunc(k)
def __repr__(self) -> str:
lst = []
- for item in self._impl._items:
- lst.append("{!r}".format(item[1]))
+ for i, k, v in self._impl._items:
+ lst.append(f"'{k}'")
body = ", ".join(lst)
- return "{}({})".format(self.__class__.__name__, body)
+ return f"<{self.__class__.__name__}({body})>"
+
+ def __and__(self, other: Iterable[object]) -> set[str]:
+ ret = set()
+ try:
+ it = iter(other)
+ except TypeError:
+ return NotImplemented
+ for key in it:
+ if not isinstance(key, str):
+ continue
+ identity = self._identfunc(key)
+ for i, k, v in self._impl._items:
+ if i == identity:
+ ret.add(k)
+ return ret
+
+ def __rand__(self, other: Iterable[_T]) -> set[_T]:
+ ret = set()
+ try:
+ it = iter(other)
+ except TypeError:
+ return NotImplemented
+ for key in it:
+ if not isinstance(key, str):
+ continue
+ identity = self._identfunc(key)
+ for i, k, v in self._impl._items:
+ if i == identity:
+ ret.add(key)
+ return cast(set[_T], ret)
+
+ def __or__(self, other: Iterable[_T]) -> set[Union[str, _T]]:
+ ret: set[Union[str, _T]] = set(self)
+ try:
+ it = iter(other)
+ except TypeError:
+ return NotImplemented
+ for key in it:
+ if not isinstance(key, str):
+ ret.add(key)
+ continue
+ identity = self._identfunc(key)
+ for i, k, v in self._impl._items:
+ if i == identity:
+ break
+ else:
+ ret.add(key)
+ return ret
+
+ def __ror__(self, other: Iterable[_T]) -> set[Union[str, _T]]:
+ try:
+ ret: set[Union[str, _T]] = set(other)
+ except TypeError:
+ return NotImplemented
+
+ tmp = set()
+ for key in ret:
+ if not isinstance(key, str):
+ continue
+ identity = self._identfunc(key)
+ tmp.add(identity)
+
+ for i, k, v in self._impl._items:
+ if i not in tmp:
+ ret.add(k)
+ return ret
+
+ def __sub__(self, other: Iterable[object]) -> set[str]:
+ ret = set(self)
+ try:
+ it = iter(other)
+ except TypeError:
+ return NotImplemented
+ for key in it:
+ if not isinstance(key, str):
+ continue
+ identity = self._identfunc(key)
+ for i, k, v in self._impl._items:
+ if i == identity:
+ ret.discard(k)
+ break
+ return ret
+
+ def __rsub__(self, other: Iterable[_T]) -> set[_T]:
+ try:
+ ret: set[_T] = set(other)
+ except TypeError:
+ return NotImplemented
+ for key in other:
+ if not isinstance(key, str):
+ continue
+ identity = self._identfunc(key)
+ for i, k, v in self._impl._items:
+ if i == identity:
+ ret.discard(key) # type: ignore[arg-type]
+ break
+ return ret
+
+ def __xor__(self, other: Iterable[_T]) -> set[Union[str, _T]]:
+ try:
+ rgt = set(other)
+ except TypeError:
+ return NotImplemented
+ ret: set[Union[str, _T]] = self - rgt # type: ignore[assignment]
+ ret |= (rgt - self)
+ return ret
+
+ __rxor__ = __xor__
+
+ def isdisjoint(self, other: Iterable[object]) -> bool:
+ for key in other:
+ if not isinstance(key, str):
+ continue
+ identity = self._identfunc(key)
+ for i, k, v in self._impl._items:
+ if i == identity:
+ return False
+ return True
+
+
+class _CSMixin:
+ def _key(self, key: str) -> str:
+ return key
+
+ def _title(self, key: str) -> str:
+ if isinstance(key, str):
+ return key
+ else:
+ raise TypeError("MultiDict keys should be either str or subclasses of str")
+
+
+class _CIMixin:
+ def _key(self, key: str) -> str:
+ if type(key) is istr:
+ return key
+ else:
+ return istr(key)
+
+ def _title(self, key: str) -> str:
+ if isinstance(key, istr):
+ ret = key.__istr_title__
+ if ret is None:
+ ret = key.title()
+ key.__istr_title__ = ret
+ return ret
+ if isinstance(key, str):
+ return key.title()
+ else:
+ raise TypeError("MultiDict keys should be either str or subclasses of str")
class _Base(MultiMapping[_V]):
_impl: _Impl[_V]
- def _title(self, key: str) -> str:
- return key
+ @abstractmethod
+ def _key(self, key: str) -> str: ...
+
+ @abstractmethod
+ def _title(self, key: str) -> str: ...
@overload
def getall(self, key: str) -> list[_V]: ...
@@ -226,15 +540,15 @@ class _Base(MultiMapping[_V]):
def keys(self) -> KeysView[str]:
"""Return a new view of the dictionary's keys."""
- return _KeysView(self._impl)
+ return _KeysView(self._impl, self._title, self._key)
def items(self) -> ItemsView[str, _V]:
"""Return a new view of the dictionary's items *(key, value) pairs)."""
- return _ItemsView(self._impl)
+ return _ItemsView(self._impl, self._title, self._key)
def values(self) -> _ValuesView[_V]:
"""Return a new view of the dictionary's values."""
- return _ValuesView(self._impl)
+ return _ValuesView(self._impl, self._title, self._key)
def __eq__(self, other: object) -> bool:
if not isinstance(other, Mapping):
@@ -266,11 +580,11 @@ class _Base(MultiMapping[_V]):
return False
def __repr__(self) -> str:
- body = ", ".join("'{}': {!r}".format(k, v) for k, v in self.items())
- return "<{}({})>".format(self.__class__.__name__, body)
+ body = ", ".join(f"'{k}': {v!r}" for i, k, v in self._impl._items)
+ return f"<{self.__class__.__name__}({body})>"
-class MultiDict(_Base[_V], MutableMultiMapping[_V]):
+class MultiDict(_CSMixin, _Base[_V], MutableMultiMapping[_V]):
"""Dictionary with the support for duplicate keys."""
def __init__(self, arg: MDArg[_V] = None, /, **kwargs: _V):
@@ -286,18 +600,9 @@ class MultiDict(_Base[_V], MutableMultiMapping[_V]):
def __reduce__(self) -> tuple[type[Self], tuple[list[tuple[str, _V]]]]:
return (self.__class__, (list(self.items()),))
- def _title(self, key: str) -> str:
- return key
-
- def _key(self, key: str) -> str:
- if isinstance(key, str):
- return key
- else:
- raise TypeError("MultiDict keys should be either str or subclasses of str")
-
def add(self, key: str, value: _V) -> None:
identity = self._title(key)
- self._impl._items.append((identity, self._key(key), value))
+ self._impl._items.append((identity, key, value))
self._impl.incr_version()
def copy(self) -> Self:
@@ -322,8 +627,11 @@ class MultiDict(_Base[_V], MutableMultiMapping[_V]):
method: Callable[[list[tuple[str, str, _V]]], None],
) -> None:
if arg:
- if isinstance(arg, (MultiDict, MultiDictProxy)) and not kwargs:
+ if isinstance(arg, (MultiDict, MultiDictProxy)):
items = arg._impl._items
+ if kwargs:
+ for key, value in kwargs.items():
+ items.append((self._title(key), key, value))
else:
if hasattr(arg, "keys"):
arg = cast(SupportsKeys[_V], arg)
@@ -332,26 +640,22 @@ class MultiDict(_Base[_V], MutableMultiMapping[_V]):
arg = list(arg)
arg.extend(list(kwargs.items()))
items = []
- for item in arg:
+ for pos, item in enumerate(arg):
if not len(item) == 2:
- raise TypeError(
- "{} takes either dict or list of (key, value) "
- "tuples".format(name)
+ raise ValueError(
+ f"multidict update sequence element #{pos}"
+ f"has length {len(item)}; 2 is required"
)
- items.append((self._title(item[0]), self._key(item[0]), item[1]))
+ items.append((self._title(item[0]), item[0], item[1]))
method(items)
else:
- method(
- [
- (self._title(key), self._key(key), value)
- for key, value in kwargs.items()
- ]
- )
+ method([(self._title(key), key, value) for key, value in kwargs.items()])
def _extend_items(self, items: Iterable[tuple[str, str, _V]]) -> None:
for identity, key, value in items:
- self.add(key, value)
+ self._impl._items.append((identity, key, value))
+ self._impl.incr_version()
def clear(self) -> None:
"""Remove all items from MultiDict."""
@@ -456,9 +760,9 @@ class MultiDict(_Base[_V], MutableMultiMapping[_V]):
def popitem(self) -> tuple[str, _V]:
"""Remove and return an arbitrary (key, value) pair."""
if self._impl._items:
- i = self._impl._items.pop(0)
+ i, k, v = self._impl._items.pop()
self._impl.incr_version()
- return i[1], i[2]
+ return self._key(k), v
else:
raise KeyError("empty multidict")
@@ -499,7 +803,6 @@ class MultiDict(_Base[_V], MutableMultiMapping[_V]):
self._impl.incr_version()
def _replace(self, key: str, value: _V) -> None:
- key = self._key(key)
identity = self._title(key)
items = self._impl._items
@@ -527,48 +830,42 @@ class MultiDict(_Base[_V], MutableMultiMapping[_V]):
i += 1
-class CIMultiDict(MultiDict[_V]):
+class CIMultiDict(_CIMixin, MultiDict[_V]):
"""Dictionary with the support for duplicate case-insensitive keys."""
- def _title(self, key: str) -> str:
- return key.title()
-
-class MultiDictProxy(_Base[_V]):
+class MultiDictProxy(_CSMixin, _Base[_V]):
"""Read-only proxy for MultiDict instance."""
def __init__(self, arg: Union[MultiDict[_V], "MultiDictProxy[_V]"]):
if not isinstance(arg, (MultiDict, MultiDictProxy)):
raise TypeError(
"ctor requires MultiDict or MultiDictProxy instance"
- ", not {}".format(type(arg))
+ f", not {type(arg)}"
)
self._impl = arg._impl
def __reduce__(self) -> NoReturn:
- raise TypeError("can't pickle {} objects".format(self.__class__.__name__))
+ raise TypeError(f"can't pickle {self.__class__.__name__} objects")
def copy(self) -> MultiDict[_V]:
"""Return a copy of itself."""
return MultiDict(self.items())
-class CIMultiDictProxy(MultiDictProxy[_V]):
+class CIMultiDictProxy(_CIMixin, MultiDictProxy[_V]):
"""Read-only proxy for CIMultiDict instance."""
def __init__(self, arg: Union[MultiDict[_V], MultiDictProxy[_V]]):
if not isinstance(arg, (CIMultiDict, CIMultiDictProxy)):
raise TypeError(
"ctor requires CIMultiDict or CIMultiDictProxy instance"
- ", not {}".format(type(arg))
+ f", not {type(arg)}"
)
self._impl = arg._impl
- def _title(self, key: str) -> str:
- return key.title()
-
def copy(self) -> CIMultiDict[_V]:
"""Return a copy of itself."""
return CIMultiDict(self.items())
diff --git a/contrib/python/multidict/multidict/_multilib/defs.h b/contrib/python/multidict/multidict/_multilib/defs.h
index 51a6639c428..9e1cd724122 100644
--- a/contrib/python/multidict/multidict/_multilib/defs.h
+++ b/contrib/python/multidict/multidict/_multilib/defs.h
@@ -6,6 +6,7 @@ extern "C" {
#endif
static PyObject *multidict_str_lower = NULL;
+static PyObject *multidict_str_canonical = NULL;
/* We link this module statically for convenience. If compiled as a shared
library instead, some compilers don't allow addresses of Python objects
diff --git a/contrib/python/multidict/multidict/_multilib/dict.h b/contrib/python/multidict/multidict/_multilib/dict.h
index 3caf83e5b4c..064101d47ea 100644
--- a/contrib/python/multidict/multidict/_multilib/dict.h
+++ b/contrib/python/multidict/multidict/_multilib/dict.h
@@ -17,6 +17,7 @@ typedef struct {
MultiDictObject *md;
} MultiDictProxyObject;
+
#ifdef __cplusplus
}
#endif
diff --git a/contrib/python/multidict/multidict/_multilib/istr.h b/contrib/python/multidict/multidict/_multilib/istr.h
index 8454f78b88b..68328054528 100644
--- a/contrib/python/multidict/multidict/_multilib/istr.h
+++ b/contrib/python/multidict/multidict/_multilib/istr.h
@@ -28,8 +28,16 @@ istr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static char *kwlist[] = {"object", "encoding", "errors", 0};
PyObject *encoding = NULL;
PyObject *errors = NULL;
- PyObject *s = NULL;
+ PyObject *canonical = NULL;
PyObject * ret = NULL;
+ if (kwds != NULL) {
+ int cmp = PyDict_Pop(kwds, multidict_str_canonical, &canonical);
+ if (cmp < 0) {
+ return NULL;
+ } else if (cmp > 0) {
+ Py_INCREF(canonical);
+ }
+ }
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO:str",
kwlist, &x, &encoding, &errors)) {
@@ -43,18 +51,57 @@ istr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
if (!ret) {
goto fail;
}
- s = PyObject_CallMethodNoArgs(ret, multidict_str_lower);
- if (!s) {
- goto fail;
+
+ if (canonical == NULL) {
+ canonical = PyObject_CallMethodNoArgs(ret, multidict_str_lower);
+ if (!canonical) {
+ goto fail;
+ }
}
- ((istrobject*)ret)->canonical = s;
- s = NULL; /* the reference is stollen by .canonical */
+ if (!PyUnicode_CheckExact(canonical)) {
+ PyObject *tmp = PyUnicode_FromObject(canonical);
+ Py_CLEAR(canonical);
+ if (tmp == NULL) {
+ goto fail;
+ }
+ canonical = tmp;
+ }
+ ((istrobject*)ret)->canonical = canonical;
return ret;
fail:
Py_XDECREF(ret);
return NULL;
}
+
+static inline PyObject *
+istr_reduce(PyObject *self)
+{
+ PyObject *str = NULL;
+ PyObject *args = NULL;
+ PyObject *result = NULL;
+
+ str = PyUnicode_FromObject(self);
+ if (str == NULL) {
+ goto ret;
+ }
+ args = PyTuple_Pack(1, str);
+ if (args == NULL) {
+ goto ret;
+ }
+ result = PyTuple_Pack(2, Py_TYPE(self), args);
+ret:
+ Py_CLEAR(str);
+ Py_CLEAR(args);
+ return result;
+}
+
+
+static PyMethodDef istr_methods[] = {
+ {"__reduce__", (PyCFunction)istr_reduce, METH_NOARGS, NULL},
+ {NULL, NULL} /* sentinel */
+};
+
static PyTypeObject istr_type = {
PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
"multidict._multidict.istr",
@@ -65,10 +112,45 @@ static PyTypeObject istr_type = {
| Py_TPFLAGS_UNICODE_SUBCLASS,
.tp_doc = istr__doc__,
.tp_base = DEFERRED_ADDRESS(&PyUnicode_Type),
+ .tp_methods = istr_methods,
.tp_new = (newfunc)istr_new,
};
+static inline PyObject *
+IStr_New(PyObject *str, PyObject *canonical)
+{
+ PyObject *args = NULL;
+ PyObject *kwds = NULL;
+ PyObject *res = NULL;
+
+ args = PyTuple_Pack(1, str);
+ if (args == NULL) {
+ goto ret;
+ }
+
+ if (canonical != NULL) {
+ kwds = PyDict_New();
+ if (kwds == NULL) {
+ goto ret;
+ }
+ if (!PyUnicode_CheckExact(canonical)) {
+ PyErr_SetString(PyExc_TypeError,
+ "'canonical' argument should be exactly str");
+ goto ret;
+ }
+ if (PyDict_SetItem(kwds, multidict_str_canonical, canonical) < 0) {
+ goto ret;
+ }
+ }
+
+ res = istr_new(&istr_type, args, kwds);
+ret:
+ Py_CLEAR(args);
+ Py_CLEAR(kwds);
+ return res;
+}
+
static inline int
istr_init(void)
{
diff --git a/contrib/python/multidict/multidict/_multilib/iter.h b/contrib/python/multidict/multidict/_multilib/iter.h
index 9ee33753826..3fd34e2ca4b 100644
--- a/contrib/python/multidict/multidict/_multilib/iter.h
+++ b/contrib/python/multidict/multidict/_multilib/iter.h
@@ -12,8 +12,7 @@ static PyTypeObject multidict_keys_iter_type;
typedef struct multidict_iter {
PyObject_HEAD
MultiDictObject *md; // MultiDict or CIMultiDict
- Py_ssize_t current;
- uint64_t version;
+ pair_list_pos_t current;
} MultidictIter;
static inline void
@@ -22,8 +21,7 @@ _init_iter(MultidictIter *it, MultiDictObject *md)
Py_INCREF(md);
it->md = md;
- it->current = 0;
- it->version = pair_list_version(&md->pairs);
+ pair_list_init_pos(&md->pairs, &it->current);
}
static inline PyObject *
@@ -78,17 +76,21 @@ multidict_items_iter_iternext(MultidictIter *self)
PyObject *value = NULL;
PyObject *ret = NULL;
- if (self->version != pair_list_version(&self->md->pairs)) {
- PyErr_SetString(PyExc_RuntimeError, "Dictionary changed during iteration");
+ int res = pair_list_next(&self->md->pairs, &self->current,
+ NULL, &key, &value);
+ if (res < 0) {
return NULL;
}
-
- if (!_pair_list_next(&self->md->pairs, &self->current, NULL, &key, &value, NULL)) {
+ if (res == 0) {
+ Py_CLEAR(key);
+ Py_CLEAR(value);
PyErr_SetNone(PyExc_StopIteration);
return NULL;
}
ret = PyTuple_Pack(2, key, value);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
if (ret == NULL) {
return NULL;
}
@@ -101,18 +103,16 @@ 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");
+ int res = pair_list_next(&self->md->pairs, &self->current,
+ NULL, NULL, &value);
+ if (res < 0) {
return NULL;
}
-
- if (!pair_list_next(&self->md->pairs, &self->current, NULL, NULL, &value)) {
+ if (res == 0) {
PyErr_SetNone(PyExc_StopIteration);
return NULL;
}
- Py_INCREF(value);
-
return value;
}
@@ -121,18 +121,16 @@ 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");
+ int res = pair_list_next(&self->md->pairs, &self->current,
+ NULL, &key, NULL);
+ if (res < 0) {
return NULL;
}
-
- if (!pair_list_next(&self->md->pairs, &self->current, NULL, &key, NULL)) {
+ if (res == 0) {
PyErr_SetNone(PyExc_StopIteration);
return NULL;
}
- Py_INCREF(key);
-
return key;
}
diff --git a/contrib/python/multidict/multidict/_multilib/pair_list.h b/contrib/python/multidict/multidict/_multilib/pair_list.h
index b23150dfad7..c5080743339 100644
--- a/contrib/python/multidict/multidict/_multilib/pair_list.h
+++ b/contrib/python/multidict/multidict/_multilib/pair_list.h
@@ -1,3 +1,5 @@
+#include "pythoncapi_compat.h"
+
#ifndef _MULTIDICT_PAIR_LIST_H
#define _MULTIDICT_PAIR_LIST_H
@@ -10,6 +12,18 @@ extern "C" {
#include <stdint.h>
#include <stdbool.h>
+/* Implementation note.
+identity always has exact PyUnicode_Type type, not a subclass.
+It guarantees that identity hashing and comparison never calls
+Python code back, and these operations has no weird side effects,
+e.g. deletion the key from multidict.
+
+Taking into account the fact that all multidict operations except
+repr(md), repr(md_proxy), or repr(view) never access to the key
+itself but identity instead, borrowed references during iteration
+over pair_list for, e.g., md.get() or md.pop() is safe.
+*/
+
typedef struct pair {
PyObject *identity; // 8
PyObject *key; // 8
@@ -42,8 +56,8 @@ typedef struct pair_list {
pair_t buffer[EMBEDDED_CAPACITY];
} pair_list_t;
-#define MIN_CAPACITY 63
-#define CAPACITY_STEP 64
+#define MIN_CAPACITY 64
+#define CAPACITY_STEP MIN_CAPACITY
/* Global counter used to set ma_version_tag field of dictionary.
* It is incremented each time that a dictionary is created and each
@@ -53,11 +67,17 @@ static uint64_t pair_list_global_version = 0;
#define NEXT_VERSION() (++pair_list_global_version)
+typedef struct pair_list_pos {
+ Py_ssize_t pos;
+ uint64_t version;
+} pair_list_pos_t;
+
+
static inline int
str_cmp(PyObject *s1, PyObject *s2)
{
PyObject *ret = PyUnicode_RichCompare(s1, s2, Py_EQ);
- if (ret == Py_True) {
+ if (Py_IsTrue(ret)) {
Py_DECREF(ret);
return 1;
}
@@ -72,21 +92,17 @@ str_cmp(PyObject *s1, PyObject *s2)
static inline PyObject *
-key_to_str(PyObject *key)
+_key_to_ident(PyObject *key)
{
- PyObject *ret;
PyTypeObject *type = Py_TYPE(key);
if (type == &istr_type) {
- ret = ((istrobject*)key)->canonical;
- Py_INCREF(ret);
- return ret;
+ return Py_NewRef(((istrobject*)key)->canonical);
}
if (PyUnicode_CheckExact(key)) {
- Py_INCREF(key);
- return key;
+ return Py_NewRef(key);
}
if (PyUnicode_Check(key)) {
- return PyObject_Str(key);
+ return PyUnicode_FromObject(key);
}
PyErr_SetString(PyExc_TypeError,
"MultiDict keys should be either str "
@@ -96,17 +112,23 @@ key_to_str(PyObject *key)
static inline PyObject *
-ci_key_to_str(PyObject *key)
+_ci_key_to_ident(PyObject *key)
{
- PyObject *ret;
PyTypeObject *type = Py_TYPE(key);
if (type == &istr_type) {
- ret = ((istrobject*)key)->canonical;
- Py_INCREF(ret);
- return ret;
+ return Py_NewRef(((istrobject*)key)->canonical);
}
if (PyUnicode_Check(key)) {
- return PyObject_CallMethodNoArgs(key, multidict_str_lower);
+ PyObject *ret = PyObject_CallMethodNoArgs(key, multidict_str_lower);
+ if (!PyUnicode_CheckExact(ret)) {
+ PyObject *tmp = PyUnicode_FromObject(ret);
+ Py_CLEAR(ret);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ ret = tmp;
+ }
+ return ret;
}
PyErr_SetString(PyExc_TypeError,
"CIMultiDict keys should be either str "
@@ -114,35 +136,59 @@ ci_key_to_str(PyObject *key)
return NULL;
}
-static inline pair_t *
-pair_list_get(pair_list_t *list, Py_ssize_t i)
+
+static inline PyObject *
+_arg_to_key(PyObject *key, PyObject *ident)
{
- pair_t *item = list->pairs + i;
- return item;
+ if (PyUnicode_Check(key)) {
+ return Py_NewRef(key);
+ }
+ PyErr_SetString(PyExc_TypeError,
+ "MultiDict keys should be either str "
+ "or subclasses of str");
+ return NULL;
+}
+
+
+static inline PyObject *
+_ci_arg_to_key(PyObject *key, PyObject *ident)
+{
+ PyTypeObject *type = Py_TYPE(key);
+ if (type == &istr_type) {
+ return Py_NewRef(key);
+ }
+ if (PyUnicode_Check(key)) {
+ return IStr_New(key, ident);
+ }
+ PyErr_SetString(PyExc_TypeError,
+ "CIMultiDict keys should be either str "
+ "or subclasses of str");
+ return NULL;
}
static inline int
-pair_list_grow(pair_list_t *list)
+pair_list_grow(pair_list_t *list, Py_ssize_t amount)
{
// Grow by one element if needed
- Py_ssize_t new_capacity;
+ Py_ssize_t capacity = ((Py_ssize_t)((list->size + amount)
+ / CAPACITY_STEP) + 1) * CAPACITY_STEP;
+
pair_t *new_pairs;
- if (list->size < list->capacity) {
+ if (list->size + amount -1 < list->capacity) {
return 0;
}
if (list->pairs == list->buffer) {
- new_pairs = PyMem_New(pair_t, MIN_CAPACITY);
+ new_pairs = PyMem_New(pair_t, (size_t)capacity);
memcpy(new_pairs, list->buffer, (size_t)list->capacity * sizeof(pair_t));
list->pairs = new_pairs;
- list->capacity = MIN_CAPACITY;
+ list->capacity = capacity;
return 0;
} else {
- new_capacity = list->capacity + CAPACITY_STEP;
- new_pairs = PyMem_Resize(list->pairs, pair_t, (size_t)new_capacity);
+ new_pairs = PyMem_Resize(list->pairs, pair_t, (size_t)capacity);
if (NULL == new_pairs) {
// Resizing error
@@ -150,7 +196,7 @@ pair_list_grow(pair_list_t *list)
}
list->pairs = new_pairs;
- list->capacity = new_capacity;
+ list->capacity = capacity;
return 0;
}
}
@@ -194,27 +240,33 @@ pair_list_shrink(pair_list_t *list)
static inline int
-_pair_list_init(pair_list_t *list, bool calc_ci_identity)
+_pair_list_init(pair_list_t *list, bool calc_ci_identity, Py_ssize_t preallocate)
{
list->calc_ci_indentity = calc_ci_identity;
- list->pairs = list->buffer;
- list->capacity = EMBEDDED_CAPACITY;
+ Py_ssize_t capacity = EMBEDDED_CAPACITY;
+ if (preallocate >= capacity) {
+ capacity = ((Py_ssize_t)(preallocate / CAPACITY_STEP) + 1) * CAPACITY_STEP;
+ list->pairs = PyMem_New(pair_t, (size_t)capacity);
+ } else {
+ list->pairs = list->buffer;
+ }
+ list->capacity = capacity;
list->size = 0;
list->version = NEXT_VERSION();
return 0;
}
static inline int
-pair_list_init(pair_list_t *list)
+pair_list_init(pair_list_t *list, Py_ssize_t size)
{
- return _pair_list_init(list, /* calc_ci_identity = */ false);
+ return _pair_list_init(list, /* calc_ci_identity = */ false, size);
}
static inline int
-ci_pair_list_init(pair_list_t *list)
+ci_pair_list_init(pair_list_t *list, Py_ssize_t size)
{
- return _pair_list_init(list, /* calc_ci_identity = */ true);
+ return _pair_list_init(list, /* calc_ci_identity = */ true, size);
}
@@ -222,22 +274,29 @@ static inline PyObject *
pair_list_calc_identity(pair_list_t *list, PyObject *key)
{
if (list->calc_ci_indentity)
- return ci_key_to_str(key);
- return key_to_str(key);
+ return _ci_key_to_ident(key);
+ return _key_to_ident(key);
+}
+
+static inline PyObject *
+pair_list_calc_key(pair_list_t *list, PyObject *key, PyObject *ident)
+{
+ if (list->calc_ci_indentity)
+ return _ci_arg_to_key(key, ident);
+ return _arg_to_key(key, ident);
}
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);
+ pair_t *pair = list->pairs + pos;
- Py_XDECREF(pair->identity);
- Py_XDECREF(pair->key);
- Py_XDECREF(pair->value);
+ Py_CLEAR(pair->identity);
+ Py_CLEAR(pair->key);
+ Py_CLEAR(pair->value);
}
/*
@@ -251,7 +310,7 @@ pair_list_dealloc(pair_list_t *list)
*/
list->size = 0;
if (list->pairs != list->buffer) {
- PyMem_Del(list->pairs);
+ PyMem_Free(list->pairs);
list->pairs = list->buffer;
list->capacity = EMBEDDED_CAPACITY;
}
@@ -266,29 +325,21 @@ pair_list_len(pair_list_t *list)
static inline int
-_pair_list_add_with_hash(pair_list_t *list,
- PyObject *identity,
- PyObject *key,
- PyObject *value,
- Py_hash_t hash)
+_pair_list_add_with_hash_steal_refs(pair_list_t *list,
+ PyObject *identity,
+ PyObject *key,
+ PyObject *value,
+ Py_hash_t hash)
{
- pair_t *pair;
-
- if (pair_list_grow(list) < 0) {
+ if (pair_list_grow(list, 1) < 0) {
return -1;
}
- pair = pair_list_get(list, list->size);
+ pair_t *pair = list->pairs + 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();
@@ -297,25 +348,34 @@ _pair_list_add_with_hash(pair_list_t *list,
return 0;
}
+static inline int
+_pair_list_add_with_hash(pair_list_t *list,
+ PyObject *identity,
+ PyObject *key,
+ PyObject *value,
+ Py_hash_t hash)
+{
+ Py_INCREF(identity);
+ Py_INCREF(key);
+ Py_INCREF(value);
+ return _pair_list_add_with_hash_steal_refs(list, identity, key, value, hash);
+}
+
static inline int
pair_list_add(pair_list_t *list,
PyObject *key,
PyObject *value)
{
- Py_hash_t hash;
- PyObject *identity = NULL;
- int ret;
-
- identity = pair_list_calc_identity(list, key);
+ PyObject *identity = pair_list_calc_identity(list, key);
if (identity == NULL) {
goto fail;
}
- hash = PyObject_Hash(identity);
+ Py_hash_t hash = PyObject_Hash(identity);
if (hash == -1) {
goto fail;
}
- ret = _pair_list_add_with_hash(list, identity, key, value, hash);
+ int ret = _pair_list_add_with_hash(list, identity, key, value, hash);
Py_DECREF(identity);
return ret;
fail:
@@ -328,10 +388,7 @@ 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);
+ pair_t *pair = list->pairs + pos;
Py_DECREF(pair->identity);
Py_DECREF(pair->key);
Py_DECREF(pair->value);
@@ -344,10 +401,10 @@ pair_list_del_at(pair_list_t *list, Py_ssize_t pos)
return 0;
}
- tail = list->size - pos;
+ Py_ssize_t 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),
+ memmove((void *)(list->pairs + pos),
+ (void *)(list->pairs + pos + 1),
sizeof(pair_t) * (size_t)tail);
return pair_list_shrink(list);
@@ -359,8 +416,6 @@ _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) {
@@ -368,11 +423,11 @@ _pair_list_drop_tail(pair_list_t *list, PyObject *identity, Py_hash_t hash,
}
for (; pos < list->size; pos++) {
- pair = pair_list_get(list, pos);
+ pair_t *pair = list->pairs + pos;
if (pair->hash != hash) {
continue;
}
- ret = str_cmp(pair->identity, identity);
+ int ret = str_cmp(pair->identity, identity);
if (ret > 0) {
if (pair_list_del_at(list, pos) < 0) {
return -1;
@@ -388,46 +443,34 @@ _pair_list_drop_tail(pair_list_t *list, PyObject *identity, Py_hash_t hash,
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 = pair_list_calc_identity(list, key);
+ PyObject *identity = pair_list_calc_identity(list, key);
if (identity == NULL) {
goto fail;
}
- hash = PyObject_Hash(identity);
+ Py_hash_t hash = PyObject_Hash(identity);
if (hash == -1) {
goto fail;
}
- ret = _pair_list_del_hash(list, identity, key, hash);
+ int ret = _pair_list_drop_tail(list, identity, hash, 0);
+
+ if (ret < 0) {
+ goto fail;
+ }
+ else if (ret == 0) {
+ PyErr_SetObject(PyExc_KeyError, key);
+ goto fail;
+ }
+ else {
+ list->version = NEXT_VERSION();
+ }
Py_DECREF(identity);
- return ret;
+ return 0;
fail:
Py_XDECREF(identity);
return -1;
@@ -441,75 +484,173 @@ pair_list_version(pair_list_t *list)
}
-static inline int
-_pair_list_next(pair_list_t *list, Py_ssize_t *ppos, PyObject **pidentity,
- PyObject **pkey, PyObject **pvalue, Py_hash_t *phash)
+static inline void
+pair_list_init_pos(pair_list_t *list, pair_list_pos_t *pos)
{
- pair_t *pair;
+ pos->pos = 0;
+ pos->version = list->version;
+}
- if (*ppos >= list->size) {
+static inline int
+pair_list_next(pair_list_t *list, pair_list_pos_t *pos,
+ PyObject **pidentity,
+ PyObject **pkey, PyObject **pvalue)
+{
+ if (pos->pos >= list->size) {
+ if (pidentity) {
+ *pidentity = NULL;
+ }
+ if (pkey) {
+ *pkey = NULL;
+ }
+ if (pvalue) {
+ *pvalue = NULL;
+ }
return 0;
}
- pair = pair_list_get(list, *ppos);
+ if (pos->version != list->version) {
+ if (pidentity) {
+ *pidentity = NULL;
+ }
+ if (pkey) {
+ *pkey = NULL;
+ }
+ if (pvalue) {
+ *pvalue = NULL;
+ }
+ PyErr_SetString(PyExc_RuntimeError, "MultiDict changed during iteration");
+ return -1;
+ }
+
+
+ pair_t *pair = list->pairs + pos->pos;
if (pidentity) {
- *pidentity = pair->identity;
+ *pidentity = Py_NewRef(pair->identity);;
}
+
if (pkey) {
- *pkey = pair->key;
+ PyObject *key = pair_list_calc_key(list, pair->key, pair->identity);
+ if (key == NULL) {
+ return -1;
+ }
+ if (key != pair->key) {
+ Py_SETREF(pair->key, key);
+ } else {
+ Py_CLEAR(key);
+ }
+ *pkey = Py_NewRef(pair->key);
}
if (pvalue) {
- *pvalue = pair->value;
- }
- if (phash) {
- *phash = pair->hash;
+ *pvalue = Py_NewRef(pair->value);
}
- *ppos += 1;
+ ++pos->pos;
return 1;
}
static inline int
-pair_list_next(pair_list_t *list, Py_ssize_t *ppos, PyObject **pidentity,
- PyObject **pkey, PyObject **pvalue)
+pair_list_next_by_identity(pair_list_t *list, pair_list_pos_t *pos,
+ PyObject *identity,
+ PyObject **pkey, PyObject **pvalue)
{
- Py_hash_t hash;
- return _pair_list_next(list, ppos, pidentity, pkey, pvalue, &hash);
+ if (pos->pos >= list->size) {
+ if (pkey) {
+ *pkey = NULL;
+ }
+ if (pvalue) {
+ *pvalue = NULL;
+ }
+ return 0;
+ }
+
+ if (pos->version != list->version) {
+ if (pkey) {
+ *pkey = NULL;
+ }
+ if (pvalue) {
+ *pvalue = NULL;
+ }
+ PyErr_SetString(PyExc_RuntimeError, "MultiDict changed during iteration");
+ return -1;
+ }
+
+
+ for (; pos->pos < list->size; ++pos->pos) {
+ pair_t *pair = list->pairs + pos->pos;
+ PyObject *ret = PyUnicode_RichCompare(identity, pair->identity, Py_EQ);
+ if (Py_IsFalse(ret)) {
+ Py_DECREF(ret);
+ continue;
+ } else if (ret == NULL) {
+ return -1;
+ } else {
+ // equals
+ Py_DECREF(ret);
+ }
+
+ if (pkey) {
+ PyObject *key = pair_list_calc_key(list, pair->key, pair->identity);
+ if (key == NULL) {
+ return -1;
+ }
+ if (key != pair->key) {
+ Py_SETREF(pair->key, key);
+ } else {
+ Py_CLEAR(key);
+ }
+ *pkey = Py_NewRef(pair->key);
+ }
+ if (pvalue) {
+ *pvalue = Py_NewRef(pair->value);
+ }
+ ++pos->pos;
+ return 1;
+ }
+ if (pkey) {
+ *pkey = NULL;
+ }
+ if (pvalue) {
+ *pvalue = NULL;
+ }
+ return 0;
}
static inline int
-pair_list_contains(pair_list_t *list, PyObject *key)
+pair_list_contains(pair_list_t *list, PyObject *key, PyObject **pret)
{
- Py_hash_t hash1, hash2;
- Py_ssize_t pos = 0;
- PyObject *ident = NULL;
- PyObject *identity = NULL;
- int tmp;
+ Py_ssize_t pos;
if (!PyUnicode_Check(key)) {
return 0;
}
- ident = pair_list_calc_identity(list, key);
+ PyObject *ident = pair_list_calc_identity(list, key);
if (ident == NULL) {
goto fail;
}
- hash1 = PyObject_Hash(ident);
- if (hash1 == -1) {
+ Py_hash_t hash = PyObject_Hash(ident);
+ if (hash == -1) {
goto fail;
}
- while (_pair_list_next(list, &pos, &identity, NULL, NULL, &hash2)) {
- if (hash1 != hash2) {
+ Py_ssize_t size = pair_list_len(list);
+
+ for(pos = 0; pos < size; ++pos) {
+ pair_t * pair = list->pairs + pos;
+ if (hash != pair->hash) {
continue;
}
- tmp = str_cmp(ident, identity);
+ int tmp = str_cmp(ident, pair->identity);
if (tmp > 0) {
Py_DECREF(ident);
+ if (pret != NULL) {
+ *pret = Py_NewRef(pair->key);
+ }
return 1;
}
else if (tmp < 0) {
@@ -518,42 +659,46 @@ pair_list_contains(pair_list_t *list, PyObject *key)
}
Py_DECREF(ident);
+ if (pret != NULL) {
+ *pret = NULL;
+ }
return 0;
fail:
Py_XDECREF(ident);
+ if (pret != NULL) {
+ *pret = NULL;
+ }
return -1;
}
-static inline PyObject *
-pair_list_get_one(pair_list_t *list, PyObject *key)
+static inline int
+pair_list_get_one(pair_list_t *list, PyObject *key, PyObject **ret)
{
- Py_hash_t hash1, hash2;
- Py_ssize_t pos = 0;
- PyObject *ident = NULL;
- PyObject *identity = NULL;
- PyObject *value = NULL;
- int tmp;
+ Py_ssize_t pos;
- ident = pair_list_calc_identity(list, key);
+ PyObject *ident = pair_list_calc_identity(list, key);
if (ident == NULL) {
goto fail;
}
- hash1 = PyObject_Hash(ident);
- if (hash1 == -1) {
+ Py_hash_t hash = PyObject_Hash(ident);
+ if (hash == -1) {
goto fail;
}
- while (_pair_list_next(list, &pos, &identity, NULL, &value, &hash2)) {
- if (hash1 != hash2) {
+ Py_ssize_t size = pair_list_len(list);
+
+ for(pos = 0; pos < size; ++pos) {
+ pair_t *pair = list->pairs + pos;
+ if (hash != pair->hash) {
continue;
}
- tmp = str_cmp(ident, identity);
+ int tmp = str_cmp(ident, pair->identity);
if (tmp > 0) {
- Py_INCREF(value);
Py_DECREF(ident);
- return value;
+ *ret = Py_NewRef(pair->value);
+ return 0;
}
else if (tmp < 0) {
goto fail;
@@ -561,52 +706,48 @@ pair_list_get_one(pair_list_t *list, PyObject *key)
}
Py_DECREF(ident);
- PyErr_SetObject(PyExc_KeyError, key);
- return NULL;
+ return 0;
fail:
Py_XDECREF(ident);
- return NULL;
+ return -1;
}
-static inline PyObject *
-pair_list_get_all(pair_list_t *list, PyObject *key)
+static inline int
+pair_list_get_all(pair_list_t *list, PyObject *key, PyObject **ret)
{
- Py_hash_t hash1, hash2;
- Py_ssize_t pos = 0;
- PyObject *ident = NULL;
- PyObject *identity = NULL;
- PyObject *value = NULL;
+ Py_ssize_t pos;
PyObject *res = NULL;
- int tmp;
- ident = pair_list_calc_identity(list, key);
+ PyObject *ident = pair_list_calc_identity(list, key);
if (ident == NULL) {
goto fail;
}
- hash1 = PyObject_Hash(ident);
- if (hash1 == -1) {
+ Py_hash_t hash = PyObject_Hash(ident);
+ if (hash == -1) {
goto fail;
}
- while (_pair_list_next(list, &pos, &identity, NULL, &value, &hash2)) {
- if (hash1 != hash2) {
+ Py_ssize_t size = pair_list_len(list);
+ for(pos = 0; pos < size; ++pos) {
+ pair_t *pair = list->pairs + pos;
+
+ if (hash != pair->hash) {
continue;
}
- tmp = str_cmp(ident, identity);
+ int 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, value) < 0) {
+ if (PyList_SetItem(res, 0, Py_NewRef(pair->value)) < 0) {
goto fail;
}
- Py_INCREF(value);
}
- else if (PyList_Append(res, value) < 0) {
+ else if (PyList_Append(res, pair->value) < 0) {
goto fail;
}
}
@@ -615,160 +756,144 @@ pair_list_get_all(pair_list_t *list, PyObject *key)
}
}
- if (res == NULL) {
- PyErr_SetObject(PyExc_KeyError, key);
+ if (res != NULL) {
+ *ret = res;
}
Py_DECREF(ident);
- return res;
+ return 0;
fail:
Py_XDECREF(ident);
Py_XDECREF(res);
- return NULL;
+ return -1;
}
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;
+ Py_ssize_t pos;
- ident = pair_list_calc_identity(list, key);
+ PyObject *ident = pair_list_calc_identity(list, key);
if (ident == NULL) {
goto fail;
}
- hash1 = PyObject_Hash(ident);
- if (hash1 == -1) {
+ Py_hash_t hash = PyObject_Hash(ident);
+ if (hash == -1) {
goto fail;
}
+ Py_ssize_t size = pair_list_len(list);
+
+ for(pos = 0; pos < size; ++pos) {
+ pair_t * pair = list->pairs + pos;
- while (_pair_list_next(list, &pos, &identity, NULL, &value2, &hash2)) {
- if (hash1 != hash2) {
+ if (hash != pair->hash) {
continue;
}
- tmp = str_cmp(ident, identity);
+ int tmp = str_cmp(ident, pair->identity);
if (tmp > 0) {
- Py_INCREF(value2);
Py_DECREF(ident);
- return value2;
+ return Py_NewRef(pair->value);
}
else if (tmp < 0) {
goto fail;
}
}
- if (_pair_list_add_with_hash(list, ident, key, value, hash1) < 0) {
+ if (_pair_list_add_with_hash(list, ident, key, value, hash) < 0) {
goto fail;
}
- Py_INCREF(value);
Py_DECREF(ident);
- return value;
+ return Py_NewRef(value);
fail:
Py_XDECREF(ident);
return NULL;
}
-static inline PyObject *
-pair_list_pop_one(pair_list_t *list, PyObject *key)
+static inline int
+pair_list_pop_one(pair_list_t *list, PyObject *key, PyObject **ret)
{
- pair_t *pair;
-
- Py_hash_t hash;
Py_ssize_t pos;
PyObject *value = NULL;
- int tmp;
- PyObject *ident = NULL;
- ident = pair_list_calc_identity(list, key);
+ PyObject *ident = pair_list_calc_identity(list, key);
if (ident == NULL) {
goto fail;
}
- hash = PyObject_Hash(ident);
+ Py_hash_t hash = PyObject_Hash(ident);
if (hash == -1) {
goto fail;
}
for (pos=0; pos < list->size; pos++) {
- pair = pair_list_get(list, pos);
+ pair_t *pair = list->pairs + pos;
if (pair->hash != hash) {
continue;
}
- tmp = str_cmp(ident, pair->identity);
+ int tmp = str_cmp(ident, pair->identity);
if (tmp > 0) {
- value = pair->value;
- Py_INCREF(value);
+ value = Py_NewRef(pair->value);
if (pair_list_del_at(list, pos) < 0) {
goto fail;
}
Py_DECREF(ident);
- return value;
+ *ret = value;
+ return 0;
}
else if (tmp < 0) {
goto fail;
}
}
- PyErr_SetObject(PyExc_KeyError, key);
- goto fail;
-
+ return 0;
fail:
Py_XDECREF(value);
Py_XDECREF(ident);
- return NULL;
+ return -1;
}
-static inline PyObject *
-pair_list_pop_all(pair_list_t *list, PyObject *key)
+static inline int
+pair_list_pop_all(pair_list_t *list, PyObject *key, PyObject ** ret)
{
- Py_hash_t hash;
Py_ssize_t pos;
- pair_t *pair;
- int tmp;
- PyObject *res = NULL;
- PyObject *ident = NULL;
+ PyObject *lst = NULL;
- ident = pair_list_calc_identity(list, key);
+ PyObject *ident = pair_list_calc_identity(list, key);
if (ident == NULL) {
goto fail;
}
- hash = PyObject_Hash(ident);
+ Py_hash_t hash = PyObject_Hash(ident);
if (hash == -1) {
goto fail;
}
if (list->size == 0) {
- PyErr_SetObject(PyExc_KeyError, ident);
- goto fail;
+ Py_DECREF(ident);
+ return 0;
}
for (pos = list->size - 1; pos >= 0; pos--) {
- pair = pair_list_get(list, pos);
+ pair_t *pair = list->pairs + pos;
if (hash != pair->hash) {
continue;
}
- tmp = str_cmp(ident, pair->identity);
+ int tmp = str_cmp(ident, pair->identity);
if (tmp > 0) {
- if (res == NULL) {
- res = PyList_New(1);
- if (res == NULL) {
+ if (lst == NULL) {
+ lst = PyList_New(1);
+ if (lst == NULL) {
goto fail;
}
- if (PyList_SetItem(res, 0, pair->value) < 0) {
+ if (PyList_SetItem(lst, 0, Py_NewRef(pair->value)) < 0) {
goto fail;
}
- Py_INCREF(pair->value);
- } else if (PyList_Append(res, pair->value) < 0) {
+ } else if (PyList_Append(lst, pair->value) < 0) {
goto fail;
}
if (pair_list_del_at(list, pos) < 0) {
@@ -780,39 +905,42 @@ pair_list_pop_all(pair_list_t *list, PyObject *key)
}
}
- if (res == NULL) {
- PyErr_SetObject(PyExc_KeyError, key);
- } else if (PyList_Reverse(res) < 0) {
- goto fail;
+ if (lst != NULL) {
+ if (PyList_Reverse(lst) < 0) {
+ goto fail;
+ }
}
+ *ret = lst;
Py_DECREF(ident);
- return res;
-
+ return 0;
fail:
Py_XDECREF(ident);
- Py_XDECREF(res);
- return NULL;
+ Py_XDECREF(lst);
+ return -1;
}
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);
+ Py_ssize_t pos = list->size - 1;
+ pair_t *pair = list->pairs + pos;
+ PyObject *key = pair_list_calc_key(list, pair->key, pair->identity);
+ if (key == NULL) {
+ return NULL;
+ }
+ PyObject *ret = PyTuple_Pack(2, key, pair->value);
+ Py_CLEAR(key);
if (ret == NULL) {
return NULL;
}
- if (pair_list_del_at(list, 0) < 0) {
+ if (pair_list_del_at(list, pos) < 0) {
Py_DECREF(ret);
return NULL;
}
@@ -824,40 +952,30 @@ pair_list_pop_item(pair_list_t *list)
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 = pair_list_calc_identity(list, key);
+ PyObject *identity = pair_list_calc_identity(list, key);
if (identity == NULL) {
goto fail;
}
- hash = PyObject_Hash(identity);
+ Py_hash_t hash = PyObject_Hash(identity);
if (hash == -1) {
goto fail;
}
for (pos = 0; pos < list->size; pos++) {
- pair = pair_list_get(list, pos);
+ pair_t *pair = list->pairs + pos;
if (hash != pair->hash) {
continue;
}
- tmp = str_cmp(identity, pair->identity);
+ int 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;
+ Py_SETREF(pair->key, Py_NewRef(key));
+ Py_SETREF(pair->value, Py_NewRef(value));
break;
}
else if (tmp < 0) {
@@ -904,15 +1022,14 @@ _dict_set_number(PyObject *dict, PyObject *key, Py_ssize_t num)
static inline int
-_pair_list_post_update(pair_list_t *list, PyObject* used_keys, Py_ssize_t pos)
+pair_list_post_update(pair_list_t *list, PyObject* used)
{
- pair_t *pair;
- PyObject *tmp;
- Py_ssize_t num;
+ PyObject *tmp = NULL;
+ Py_ssize_t pos;
- for (; pos < list->size; pos++) {
- pair = pair_list_get(list, pos);
- int status = PyDict_GetItemRef(used_keys, pair->identity, &tmp);
+ for (pos = 0; pos < list->size; pos++) {
+ pair_t *pair = list->pairs + pos;
+ int status = PyDict_GetItemRef(used, pair->identity, &tmp);
if (status == -1) {
// exception set
return -1;
@@ -922,7 +1039,7 @@ _pair_list_post_update(pair_list_t *list, PyObject* used_keys, Py_ssize_t pos)
continue;
}
- num = PyLong_AsSsize_t(tmp);
+ Py_ssize_t num = PyLong_AsSsize_t(tmp);
Py_DECREF(tmp);
if (num == -1) {
if (!PyErr_Occurred()) {
@@ -947,16 +1064,14 @@ _pair_list_post_update(pair_list_t *list, PyObject* used_keys, Py_ssize_t pos)
// TODO: need refactoring function name
static inline int
_pair_list_update(pair_list_t *list, PyObject *key,
- PyObject *value, PyObject *used_keys,
+ PyObject *value, PyObject *used,
PyObject *identity, Py_hash_t hash)
{
PyObject *item = NULL;
- pair_t *pair = NULL;
Py_ssize_t pos;
int found;
- int ident_cmp_res;
- int status = PyDict_GetItemRef(used_keys, identity, &item);
+ int status = PyDict_GetItemRef(used, identity, &item);
if (status == -1) {
// exception set
return -1;
@@ -978,22 +1093,17 @@ _pair_list_update(pair_list_t *list, PyObject *key,
found = 0;
for (; pos < list->size; pos++) {
- pair = pair_list_get(list, pos);
+ pair_t *pair = list->pairs + pos;
if (pair->hash != hash) {
continue;
}
- ident_cmp_res = str_cmp(pair->identity, identity);
+ int 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;
+ Py_SETREF(pair->key, Py_NewRef(key));
+ Py_SETREF(pair->value, Py_NewRef(value));
- if (_dict_set_number(used_keys, pair->identity, pos + 1) < 0) {
+ if (_dict_set_number(used, pair->identity, pos + 1) < 0) {
return -1;
}
@@ -1009,7 +1119,7 @@ _pair_list_update(pair_list_t *list, PyObject *key,
if (_pair_list_add_with_hash(list, identity, key, value, hash) < 0) {
return -1;
}
- if (_dict_set_number(used_keys, identity, list->size) < 0) {
+ if (_dict_set_number(used, identity, list->size) < 0) {
return -1;
}
}
@@ -1019,175 +1129,289 @@ _pair_list_update(pair_list_t *list, PyObject *key,
static inline int
-pair_list_update(pair_list_t *list, pair_list_t *other)
+pair_list_update_from_pair_list(pair_list_t *list, PyObject* used, pair_list_t *other)
{
- PyObject *used_keys = NULL;
- pair_t *pair = NULL;
-
Py_ssize_t pos;
- if (other->size == 0) {
- return 0;
+ for (pos = 0; pos < other->size; pos++) {
+ pair_t *pair = other->pairs + pos;
+ if (used != NULL) {
+ if (_pair_list_update(list, pair->key, pair->value, used,
+ pair->identity, pair->hash) < 0) {
+ goto fail;
+ }
+ } else {
+ if (_pair_list_add_with_hash(list, pair->identity, pair->key,
+ pair->value, pair->hash) < 0) {
+ goto fail;
+ }
+ }
}
+ return 0;
+fail:
+ return -1;
+}
- used_keys = PyDict_New();
- if (used_keys == NULL) {
- return -1;
- }
+static inline int
+pair_list_update_from_dict(pair_list_t *list, PyObject* used, PyObject *kwds)
+{
+ Py_ssize_t pos = 0;
+ PyObject *identity = NULL;
+ PyObject *key = NULL;
+ PyObject *value = NULL;
- 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) {
+ while(PyDict_Next(kwds, &pos, &key, &value)) {
+ Py_INCREF(key);
+ identity = pair_list_calc_identity(list, key);
+ if (identity == NULL) {
goto fail;
}
+ Py_hash_t hash = PyObject_Hash(identity);
+ if (hash == -1) {
+ goto fail;
+ }
+ if (used != NULL) {
+ if (_pair_list_update(list, key, value, used, identity, hash) < 0) {
+ goto fail;
+ }
+ } else {
+ if (_pair_list_add_with_hash(list, identity, key, value, hash) < 0) {
+ goto fail;
+ }
+ }
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
}
+ return 0;
+fail:
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ return -1;
+}
- if (_pair_list_post_update(list, used_keys, 0) < 0) {
- goto fail;
- }
+static inline void _err_not_sequence(Py_ssize_t i)
+{
+ PyErr_Format(PyExc_TypeError,
+ "multidict cannot convert sequence element #%zd"
+ " to a sequence",
+ i);
+}
- Py_DECREF(used_keys);
- return 0;
+static inline void _err_bad_length(Py_ssize_t i, Py_ssize_t n)
+{
+ PyErr_Format(PyExc_ValueError,
+ "multidict update sequence element #%zd "
+ "has length %zd; 2 is required",
+ i, n);
+}
+
+static inline void _err_cannot_fetch(Py_ssize_t i, const char * name)
+{
+ PyErr_Format(PyExc_ValueError,
+ "multidict update sequence element #%zd's "
+ "%s could not be fetched", name, i);
+}
+
+static int _pair_list_parse_item(Py_ssize_t i, PyObject *item,
+ PyObject **pkey, PyObject **pvalue)
+{
+ Py_ssize_t n;
+
+ if (PyList_CheckExact(item)) {
+ n = PyList_GET_SIZE(item);
+ if (n != 2) {
+ _err_bad_length(i, n);
+ goto fail;
+ }
+ *pkey = Py_NewRef(PyList_GET_ITEM(item, 0));
+ *pvalue = Py_NewRef(PyList_GET_ITEM(item, 1));
+ } else if (PyTuple_CheckExact(item)) {
+ n = PyTuple_GET_SIZE(item);
+ if (n != 2) {
+ _err_bad_length(i, n);
+ goto fail;
+ }
+ *pkey = Py_NewRef(PyTuple_GET_ITEM(item, 0));
+ *pvalue = Py_NewRef(PyTuple_GET_ITEM(item, 1));
+ } else {
+ if (!PySequence_Check(item)) {
+ _err_not_sequence(i);
+ goto fail;
+ }
+ n = PySequence_Size(item);
+ if (n != 2) {
+ _err_bad_length(i, n);
+ goto fail;
+ }
+ *pkey = PySequence_ITEM(item, 0);
+ *pvalue = PySequence_ITEM(item, 1);
+ if (*pkey == NULL) {
+ _err_cannot_fetch(i, "key");
+ goto fail;
+ }
+ if (*pvalue == NULL) {
+ _err_cannot_fetch(i, "value");
+ goto fail;
+ }
+ }
+ return 0;
fail:
- Py_XDECREF(used_keys);
+ Py_CLEAR(*pkey);
+ Py_CLEAR(*pvalue);
return -1;
}
static inline int
-pair_list_update_from_seq(pair_list_t *list, PyObject *seq)
+pair_list_update_from_seq(pair_list_t *list, PyObject *used, PyObject *seq)
{
- PyObject *it = NULL; // iter(seq)
- PyObject *fast = NULL; // item as a 2-tuple or 2-list
+ PyObject *it = NULL;
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;
+ Py_ssize_t size = -1;
- it = PyObject_GetIter(seq);
- if (it == NULL) {
- return -1;
- }
+ enum {LIST, TUPLE, ITER} kind;
- used_keys = PyDict_New();
- if (used_keys == NULL) {
- goto fail_1;
+ if (PyList_CheckExact(seq)) {
+ kind = LIST;
+ size = PyList_GET_SIZE(seq);
+ } else if (PyTuple_CheckExact(seq)) {
+ kind = TUPLE;
+ size = PyTuple_GET_SIZE(seq);
+ } else {
+ kind = ITER;
+ it = PyObject_GetIter(seq);
+ if (it == NULL) {
+ goto fail;
+ }
}
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;
+ switch (kind) {
+ case LIST:
+ if (i >= size) {
+ goto exit;
+ }
+ item = PyList_GET_ITEM(seq, i);
+ if (item == NULL) {
+ goto fail;
}
+ Py_INCREF(item);
break;
- }
-
- // Convert item to sequence, and verify length 2.
-#ifdef Py_GIL_DISABLED
- if (!PySequence_Check(item)) {
-#else
- fast = PySequence_Fast(item, "");
- if (fast == NULL) {
- if (PyErr_ExceptionMatches(PyExc_TypeError)) {
-#endif
- PyErr_Format(PyExc_TypeError,
- "multidict cannot convert sequence element #%zd"
- " to a sequence",
- i);
-#ifndef Py_GIL_DISABLED
+ case TUPLE:
+ if (i >= size) {
+ goto exit;
+ }
+ item = PyTuple_GET_ITEM(seq, i);
+ if (item == NULL) {
+ goto fail;
+ }
+ Py_INCREF(item);
+ break;
+ case ITER:
+ item = PyIter_Next(it);
+ if (item == NULL) {
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ goto exit;
}
-#endif
- goto fail_1;
}
-#ifdef Py_GIL_DISABLED
- n = PySequence_Size(item);
-#else
- n = PySequence_Fast_GET_SIZE(fast);
-#endif
- if (n != 2) {
- PyErr_Format(PyExc_ValueError,
- "multidict update sequence element #%zd "
- "has length %zd; 2 is required",
- i, n);
- goto fail_1;
+ if (_pair_list_parse_item(i, item, &key, &value) < 0) {
+ goto fail;
}
-#ifdef Py_GIL_DISABLED
- key = PySequence_ITEM(item, 0);
- if (key == NULL) {
- PyErr_Format(PyExc_ValueError,
- "multidict update sequence element #%zd's "
- "key could not be fetched", i);
- goto fail_1;
- }
- value = PySequence_ITEM(item, 1);
- if (value == NULL) {
- PyErr_Format(PyExc_ValueError,
- "multidict update sequence element #%zd's "
- "value could not be fetched", i);
- goto fail_1;
- }
-#else
- key = PySequence_Fast_GET_ITEM(fast, 0);
- value = PySequence_Fast_GET_ITEM(fast, 1);
- Py_INCREF(key);
- Py_INCREF(value);
-#endif
-
identity = pair_list_calc_identity(list, key);
if (identity == NULL) {
- goto fail_1;
+ goto fail;
}
- hash = PyObject_Hash(identity);
+ Py_hash_t hash = PyObject_Hash(identity);
if (hash == -1) {
- goto fail_1;
+ goto fail;
}
- if (_pair_list_update(list, key, value, used_keys, identity, hash) < 0) {
- goto fail_1;
+ if (used) {
+ if (_pair_list_update(list, key, value, used, identity, hash) < 0) {
+ goto fail;
+ }
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ } else {
+ if (_pair_list_add_with_hash_steal_refs(list, identity,
+ key, value, hash) < 0) {
+ goto fail;
+ }
+ identity = NULL;
+ key = NULL;
+ value = NULL;
}
+ Py_CLEAR(item);
+ }
- Py_DECREF(key);
- Py_DECREF(value);
-#ifndef Py_GIL_DISABLED
- Py_DECREF(fast);
-#endif
- Py_DECREF(item);
- Py_DECREF(identity);
+exit:
+ Py_CLEAR(it);
+ return 0;
+
+fail:
+ Py_CLEAR(identity);
+ Py_CLEAR(it);
+ Py_CLEAR(item);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ return -1;
+}
+
+
+static inline int
+pair_list_eq(pair_list_t *list, pair_list_t *other)
+{
+ Py_ssize_t pos;
+
+ if (list == other) {
+ return 1;
}
- if (_pair_list_post_update(list, used_keys, 0) < 0) {
- goto fail_2;
+ Py_ssize_t size = pair_list_len(list);
+
+ if (size != pair_list_len(other)) {
+ return 0;
}
- Py_DECREF(it);
- Py_DECREF(used_keys);
- return 0;
+ for(pos = 0; pos < size; ++pos) {
+ pair_t *pair1 = list->pairs + pos;
+ pair_t *pair2 = other->pairs +pos;
-fail_1:
- Py_XDECREF(key);
- Py_XDECREF(value);
- Py_XDECREF(fast);
- Py_XDECREF(item);
- Py_XDECREF(identity);
+ if (pair1->hash != pair2->hash) {
+ return 0;
+ }
-fail_2:
- Py_XDECREF(it);
- Py_XDECREF(used_keys);
- return -1;
+ int cmp = PyObject_RichCompareBool(pair1->identity, pair2->identity, Py_EQ);
+ if (cmp < 0) {
+ return -1;
+ };
+ if (cmp == 0) {
+ return 0;
+ }
+
+ cmp = PyObject_RichCompareBool(pair1->value, pair2->value, Py_EQ);
+ if (cmp < 0) {
+ return -1;
+ };
+ if (cmp == 0) {
+ return 0;
+ }
+ }
+
+ return 1;
}
static inline int
@@ -1197,9 +1421,7 @@ pair_list_eq_to_mapping(pair_list_t *list, PyObject *other)
PyObject *avalue = NULL;
PyObject *bvalue;
- Py_ssize_t pos, other_len;
-
- int eq;
+ Py_ssize_t other_len;
if (!PyMapping_Check(other)) {
PyErr_Format(PyExc_TypeError,
@@ -1216,19 +1438,32 @@ pair_list_eq_to_mapping(pair_list_t *list, PyObject *other)
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;
- }
+ pair_list_pos_t pos;
+ pair_list_init_pos(list, &pos);
+
+ for(;;) {
+ int ret = pair_list_next(list, &pos, NULL, &key, &avalue);
+ if (ret < 0) {
+ return -1;
+ }
+ if (ret == 0) {
+ break;
+ }
+ ret = PyMapping_GetOptionalItem(other, key, &bvalue);
+ Py_CLEAR(key);
+ if (ret < 0) {
+ Py_CLEAR(avalue);
return -1;
}
- eq = PyObject_RichCompareBool(avalue, bvalue, Py_EQ);
- Py_DECREF(bvalue);
+ if (bvalue == NULL) {
+ Py_CLEAR(avalue);
+ return 0;
+ }
+
+ int eq = PyObject_RichCompareBool(avalue, bvalue, Py_EQ);
+ Py_CLEAR(bvalue);
+ Py_CLEAR(avalue);
if (eq <= 0) {
return eq;
@@ -1239,6 +1474,81 @@ pair_list_eq_to_mapping(pair_list_t *list, PyObject *other)
}
+static inline PyObject *
+pair_list_repr(pair_list_t *list, PyObject *name,
+ bool show_keys, bool show_values)
+{
+ PyObject *key = NULL;
+ PyObject *value = NULL;
+
+ bool comma = false;
+ Py_ssize_t pos;
+ uint64_t version = list->version;
+
+ PyUnicodeWriter *writer = PyUnicodeWriter_Create(1024);
+ if (writer == NULL)
+ return NULL;
+
+ if (PyUnicodeWriter_WriteChar(writer, '<') <0)
+ goto fail;
+ if (PyUnicodeWriter_WriteStr(writer, name) <0)
+ goto fail;
+ if (PyUnicodeWriter_WriteChar(writer, '(') <0)
+ goto fail;
+
+ for (pos = 0; pos < list->size; ++pos) {
+ if (version != list->version) {
+ PyErr_SetString(PyExc_RuntimeError, "MultiDict changed during iteration");
+ return NULL;
+ }
+ pair_t *pair = list->pairs + pos;
+ key = Py_NewRef(pair->key);
+ value = Py_NewRef(pair->value);
+
+ if (comma) {
+ if (PyUnicodeWriter_WriteChar(writer, ',') <0)
+ goto fail;
+ if (PyUnicodeWriter_WriteChar(writer, ' ') <0)
+ goto fail;
+ }
+ if (show_keys) {
+ if (PyUnicodeWriter_WriteChar(writer, '\'') <0)
+ goto fail;
+ /* Don't need to convert key to istr, the text is the same*/
+ if (PyUnicodeWriter_WriteStr(writer, key) <0)
+ goto fail;
+ if (PyUnicodeWriter_WriteChar(writer, '\'') <0)
+ goto fail;
+ }
+ if (show_keys && show_values) {
+ if (PyUnicodeWriter_WriteChar(writer, ':') <0)
+ goto fail;
+ if (PyUnicodeWriter_WriteChar(writer, ' ') <0)
+ goto fail;
+ }
+ if (show_values) {
+ if (PyUnicodeWriter_WriteRepr(writer, value) <0)
+ goto fail;
+ }
+
+ comma = true;
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ }
+
+ if (PyUnicodeWriter_WriteChar(writer, ')') <0)
+ goto fail;
+ if (PyUnicodeWriter_WriteChar(writer, '>') <0)
+ goto fail;
+ return PyUnicodeWriter_Finish(writer);
+fail:
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ PyUnicodeWriter_Discard(writer);
+}
+
+
+
/***********************************************************************/
static inline int
@@ -1248,7 +1558,7 @@ pair_list_traverse(pair_list_t *list, visitproc visit, void *arg)
Py_ssize_t pos;
for (pos = 0; pos < list->size; pos++) {
- pair = pair_list_get(list, pos);
+ pair = list->pairs + pos;
// Don't need traverse the identity: it is a terminal
Py_VISIT(pair->key);
Py_VISIT(pair->value);
@@ -1270,14 +1580,14 @@ pair_list_clear(pair_list_t *list)
list->version = NEXT_VERSION();
for (pos = 0; pos < list->size; pos++) {
- pair = pair_list_get(list, pos);
+ pair = list->pairs + 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);
+ PyMem_Free(list->pairs);
list->pairs = list->buffer;
}
diff --git a/contrib/python/multidict/multidict/_multilib/parser.h b/contrib/python/multidict/multidict/_multilib/parser.h
new file mode 100644
index 00000000000..a804018cf1d
--- /dev/null
+++ b/contrib/python/multidict/multidict/_multilib/parser.h
@@ -0,0 +1,147 @@
+#ifndef _MULTIDICT_PARSER_H
+#define _MULTIDICT_PARSER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static int raise_unexpected_kwarg(const char *fname, PyObject* argname)
+{
+ PyErr_Format(PyExc_TypeError,
+ "%.150s() got an unexpected keyword argument '%.150U'",
+ fname, argname);
+ return -1;
+}
+
+static int raise_missing_posarg(const char *fname, const char* argname)
+{
+ PyErr_Format(PyExc_TypeError,
+ "%.150s() missing 1 required positional argument: '%.150s'",
+ fname, argname);
+ return -1;
+}
+
+
+
+/* Parse FASTCALL|METH_KEYWORDS arguments as two args,
+the first arg is mandatory and the second one is optional.
+If the second arg is not passed it remains NULL pointer.
+
+The parser accepts three forms:
+1. all positional args,
+2. fist positional, second keyword-arg
+3. all named keyword args.
+*/
+
+static int parse2(const char* fname,
+ PyObject*const *args,
+ Py_ssize_t nargs,
+ PyObject *kwnames,
+ Py_ssize_t minargs,
+ const char* arg1name,
+ PyObject **arg1,
+ const char* arg2name,
+ PyObject **arg2
+)
+{
+ assert(minargs>=1);
+ assert(minargs<=2);
+
+ if (kwnames != NULL) {
+ Py_ssize_t kwsize = PyTuple_Size(kwnames);
+ if (kwsize < 0) {
+ return -1;
+ }
+ PyObject *argname; // borrowed ref
+ if (kwsize == 2) {
+ /* All args are passed by keyword, possible combinations:
+ arg1, arg2 and arg2, arg1 */
+ argname = PyTuple_GetItem(kwnames, 0);
+ if (argname == NULL) {
+ return -1;
+ }
+ if (PyUnicode_CompareWithASCIIString(argname, arg1name) == 0) {
+ argname = PyTuple_GetItem(kwnames, 1);
+ if (argname == NULL) {
+ return -1;
+ }
+ if (PyUnicode_CompareWithASCIIString(argname, arg2name) == 0) {
+ *arg1 = args[0];
+ *arg2 = args[1];
+ return 0;
+ } else {
+ return raise_unexpected_kwarg(fname, argname);
+ }
+ } else if (PyUnicode_CompareWithASCIIString(argname, arg2name) == 0) {
+ argname = PyTuple_GetItem(kwnames, 1);
+ if (argname == NULL) {
+ return -1;
+ }
+ if (PyUnicode_CompareWithASCIIString(argname, arg1name) == 0) {
+ *arg1 = args[1];
+ *arg2 = args[0];
+ return 0;
+ } else {
+ return raise_unexpected_kwarg(fname, argname);
+ }
+ } else {
+ return raise_unexpected_kwarg(fname, argname);
+ }
+ } else {
+ // kwsize == 1
+ argname = PyTuple_GetItem(kwnames, 0);
+ if (argname == NULL) {
+ return -1;
+ }
+ if (nargs == 1) {
+ if (PyUnicode_CompareWithASCIIString(argname, arg2name) == 0) {
+ *arg1 = args[0];
+ *arg2 = args[1];
+ return 0;
+ } else {
+ return raise_unexpected_kwarg(fname, argname);
+ }
+ } else {
+ // nargs == 0
+ if (PyUnicode_CompareWithASCIIString(argname, arg1name) == 0) {
+ *arg1 = args[0];
+ *arg2 = NULL;
+ return 0;
+ } else {
+ return raise_missing_posarg(fname, arg1name);
+ }
+ }
+ }
+ } else {
+ if (nargs < 1) {
+ PyErr_Format(PyExc_TypeError,
+ "%.150s() missing 1 required positional argument: '%s'",
+ fname, arg1name);
+ return -1;
+ }
+ if (nargs < minargs || nargs > 2) {
+ const char* txt;
+ if (minargs == 2) {
+ txt = "from 1 to 2 positional arguments";
+ } else {
+ txt = "exactly 1 positional argument";
+ }
+ PyErr_Format(PyExc_TypeError,
+ "%.150s() takes %s but %zd were given",
+ fname, txt, nargs);
+ return -1;
+ }
+ *arg1 = args[0];
+ if (nargs == 2) {
+ *arg2 = args[1];
+ } else {
+ *arg2 = NULL;
+ }
+ return 0;
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/contrib/python/multidict/multidict/_multilib/pythoncapi_compat.h b/contrib/python/multidict/multidict/_multilib/pythoncapi_compat.h
index 971981993ba..4b179e49319 100644
--- a/contrib/python/multidict/multidict/_multilib/pythoncapi_compat.h
+++ b/contrib/python/multidict/multidict/_multilib/pythoncapi_compat.h
@@ -7,10 +7,7 @@
// https://github.com/python/pythoncapi_compat
//
// Latest version:
-// https://raw.githubusercontent.com/python/pythoncapi_compat/master/pythoncapi_compat.h
-//
-// The vendored version comes from commit:
-// https://raw.githubusercontent.com/python/pythoncapi-compat/2d18aecd7b2f549d38a13e27b682ea4966f37bd8/pythoncapi_compat.h
+// https://raw.githubusercontent.com/python/pythoncapi-compat/main/pythoncapi_compat.h
//
// SPDX-License-Identifier: 0BSD
@@ -22,11 +19,15 @@ extern "C" {
#endif
#include <Python.h>
+#include <stddef.h> // offsetof()
// Python 3.11.0b4 added PyFrame_Back() to Python.h
#if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION)
# include "frameobject.h" // PyFrameObject, PyFrame_GetBack()
#endif
+#if PY_VERSION_HEX < 0x030C00A3
+# include <structmember.h> // T_SHORT, READONLY
+#endif
#ifndef _Py_CAST
@@ -36,11 +37,13 @@ extern "C" {
// Static inline functions should use _Py_NULL rather than using directly NULL
// to prevent C++ compiler warnings. On C23 and newer and on C++11 and newer,
// _Py_NULL is defined as nullptr.
-#if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) \
- || (defined(__cplusplus) && __cplusplus >= 201103)
-# define _Py_NULL nullptr
-#else
-# define _Py_NULL NULL
+#ifndef _Py_NULL
+# if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) \
+ || (defined(__cplusplus) && __cplusplus >= 201103)
+# define _Py_NULL nullptr
+# else
+# define _Py_NULL NULL
+# endif
#endif
// Cast argument to PyObject* type.
@@ -48,6 +51,13 @@ extern "C" {
# define _PyObject_CAST(op) _Py_CAST(PyObject*, op)
#endif
+#ifndef Py_BUILD_ASSERT
+# define Py_BUILD_ASSERT(cond) \
+ do { \
+ (void)sizeof(char [1 - 2 * !(cond)]); \
+ } while(0)
+#endif
+
// bpo-42262 added Py_NewRef() to Python 3.10.0a3
#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef)
@@ -71,6 +81,37 @@ static inline PyObject* _Py_XNewRef(PyObject *obj)
#endif
+// bpo-39573 added Py_SET_REFCNT() to Python 3.9.0a4
+#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_REFCNT)
+static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt)
+{
+ ob->ob_refcnt = refcnt;
+}
+#define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt)
+#endif
+
+
+// Py_SETREF() and Py_XSETREF() were added to Python 3.5.2.
+// It is excluded from the limited C API.
+#if (PY_VERSION_HEX < 0x03050200 && !defined(Py_SETREF)) && !defined(Py_LIMITED_API)
+#define Py_SETREF(dst, src) \
+ do { \
+ PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
+ PyObject *_tmp_dst = (*_tmp_dst_ptr); \
+ *_tmp_dst_ptr = _PyObject_CAST(src); \
+ Py_DECREF(_tmp_dst); \
+ } while (0)
+
+#define Py_XSETREF(dst, src) \
+ do { \
+ PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
+ PyObject *_tmp_dst = (*_tmp_dst_ptr); \
+ *_tmp_dst_ptr = _PyObject_CAST(src); \
+ Py_XDECREF(_tmp_dst); \
+ } while (0)
+#endif
+
+
// bpo-43753 added Py_Is(), Py_IsNone(), Py_IsTrue() and Py_IsFalse()
// to Python 3.10.0b1.
#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_Is)
@@ -87,7 +128,28 @@ static inline PyObject* _Py_XNewRef(PyObject *obj)
#endif
-#if defined(PYPY_VERSION)
+// bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4
+#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE)
+static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type)
+{
+ ob->ob_type = type;
+}
+#define Py_SET_TYPE(ob, type) _Py_SET_TYPE(_PyObject_CAST(ob), type)
+#endif
+
+
+// bpo-39573 added Py_SET_SIZE() to Python 3.9.0a4
+#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE)
+static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size)
+{
+ ob->ob_size = size;
+}
+#define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject*)(ob), size)
+#endif
+
+
+// bpo-40421 added PyFrame_GetCode() to Python 3.9.0b1
+#if PY_VERSION_HEX < 0x030900B1 || defined(PYPY_VERSION)
static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame)
{
assert(frame != _Py_NULL);
@@ -103,6 +165,16 @@ static inline PyCodeObject* _PyFrame_GetCodeBorrow(PyFrameObject *frame)
return code;
}
+
+// bpo-40421 added PyFrame_GetBack() to Python 3.9.0b1
+#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION)
+static inline PyFrameObject* PyFrame_GetBack(PyFrameObject *frame)
+{
+ assert(frame != _Py_NULL);
+ return _Py_CAST(PyFrameObject*, Py_XNewRef(frame->f_back));
+}
+#endif
+
#if !defined(PYPY_VERSION)
static inline PyFrameObject* _PyFrame_GetBackBorrow(PyFrameObject *frame)
{
@@ -117,9 +189,13 @@ static inline PyFrameObject* _PyFrame_GetBackBorrow(PyFrameObject *frame)
#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION)
static inline PyObject* PyFrame_GetLocals(PyFrameObject *frame)
{
+#if PY_VERSION_HEX >= 0x030400B1
if (PyFrame_FastToLocalsWithError(frame) < 0) {
return NULL;
}
+#else
+ PyFrame_FastToLocals(frame);
+#endif
return Py_NewRef(frame->f_locals);
}
#endif
@@ -172,14 +248,22 @@ static inline PyObject* PyFrame_GetVar(PyFrameObject *frame, PyObject *name)
if (locals == NULL) {
return NULL;
}
+#if PY_VERSION_HEX >= 0x03000000
value = PyDict_GetItemWithError(locals, name);
+#else
+ value = _PyDict_GetItemWithError(locals, name);
+#endif
Py_DECREF(locals);
if (value == NULL) {
if (PyErr_Occurred()) {
return NULL;
}
+#if PY_VERSION_HEX >= 0x03000000
PyErr_Format(PyExc_NameError, "variable %R does not exist", name);
+#else
+ PyErr_SetString(PyExc_NameError, "variable does not exist");
+#endif
return NULL;
}
return Py_NewRef(value);
@@ -193,7 +277,11 @@ static inline PyObject*
PyFrame_GetVarString(PyFrameObject *frame, const char *name)
{
PyObject *name_obj, *value;
+#if PY_VERSION_HEX >= 0x03000000
name_obj = PyUnicode_FromString(name);
+#else
+ name_obj = PyString_FromString(name);
+#endif
if (name_obj == NULL) {
return NULL;
}
@@ -204,7 +292,8 @@ PyFrame_GetVarString(PyFrameObject *frame, const char *name)
#endif
-#if defined(PYPY_VERSION)
+// bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5
+#if PY_VERSION_HEX < 0x030900A5 || (defined(PYPY_VERSION) && PY_VERSION_HEX < 0x030B0000)
static inline PyInterpreterState *
PyThreadState_GetInterpreter(PyThreadState *tstate)
{
@@ -213,6 +302,16 @@ PyThreadState_GetInterpreter(PyThreadState *tstate)
}
#endif
+
+// bpo-40429 added PyThreadState_GetFrame() to Python 3.9.0b1
+#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION)
+static inline PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate)
+{
+ assert(tstate != _Py_NULL);
+ return _Py_CAST(PyFrameObject *, Py_XNewRef(tstate->frame));
+}
+#endif
+
#if !defined(PYPY_VERSION)
static inline PyFrameObject*
_PyThreadState_GetFrameBorrow(PyThreadState *tstate)
@@ -224,7 +323,8 @@ _PyThreadState_GetFrameBorrow(PyThreadState *tstate)
#endif
-#if defined(PYPY_VERSION)
+// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a5
+#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION)
static inline PyInterpreterState* PyInterpreterState_Get(void)
{
PyThreadState *tstate;
@@ -242,6 +342,16 @@ static inline PyInterpreterState* PyInterpreterState_Get(void)
}
#endif
+
+// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a6
+#if 0x030700A1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION)
+static inline uint64_t PyThreadState_GetID(PyThreadState *tstate)
+{
+ assert(tstate != _Py_NULL);
+ return tstate->id;
+}
+#endif
+
// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2
#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION)
static inline void PyThreadState_EnterTracing(PyThreadState *tstate)
@@ -271,6 +381,27 @@ static inline void PyThreadState_LeaveTracing(PyThreadState *tstate)
#endif
+// bpo-37194 added PyObject_CallNoArgs() to Python 3.9.0a1
+// PyObject_CallNoArgs() added to PyPy 3.9.16-v7.3.11
+#if !defined(PyObject_CallNoArgs) && PY_VERSION_HEX < 0x030900A1
+static inline PyObject* PyObject_CallNoArgs(PyObject *func)
+{
+ return PyObject_CallFunctionObjArgs(func, NULL);
+}
+#endif
+
+
+// bpo-39245 made PyObject_CallOneArg() public (previously called
+// _PyObject_CallOneArg) in Python 3.9.0a4
+// PyObject_CallOneArg() added to PyPy 3.9.16-v7.3.11
+#if !defined(PyObject_CallOneArg) && PY_VERSION_HEX < 0x030900A4
+static inline PyObject* PyObject_CallOneArg(PyObject *func, PyObject *arg)
+{
+ return PyObject_CallFunctionObjArgs(func, arg, NULL);
+}
+#endif
+
+
// bpo-1635741 added PyModule_AddObjectRef() to Python 3.10.0a3
#if PY_VERSION_HEX < 0x030A00A3
static inline int
@@ -296,10 +427,63 @@ PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value)
#endif
+// bpo-40024 added PyModule_AddType() to Python 3.9.0a5
+#if PY_VERSION_HEX < 0x030900A5
+static inline int PyModule_AddType(PyObject *module, PyTypeObject *type)
+{
+ const char *name, *dot;
+
+ if (PyType_Ready(type) < 0) {
+ return -1;
+ }
+
+ // inline _PyType_Name()
+ name = type->tp_name;
+ assert(name != _Py_NULL);
+ dot = strrchr(name, '.');
+ if (dot != _Py_NULL) {
+ name = dot + 1;
+ }
+
+ return PyModule_AddObjectRef(module, name, _PyObject_CAST(type));
+}
+#endif
+
+
+// bpo-40241 added PyObject_GC_IsTracked() to Python 3.9.0a6.
+// bpo-4688 added _PyObject_GC_IS_TRACKED() to Python 2.7.0a2.
+#if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION)
+static inline int PyObject_GC_IsTracked(PyObject* obj)
+{
+ return (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj));
+}
+#endif
+
+// bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9.0a6.
+// bpo-18112 added _PyGCHead_FINALIZED() to Python 3.4.0 final.
+#if PY_VERSION_HEX < 0x030900A6 && PY_VERSION_HEX >= 0x030400F0 && !defined(PYPY_VERSION)
+static inline int PyObject_GC_IsFinalized(PyObject *obj)
+{
+ PyGC_Head *gc = _Py_CAST(PyGC_Head*, obj) - 1;
+ return (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(gc));
+}
+#endif
+
+
+// bpo-39573 added Py_IS_TYPE() to Python 3.9.0a4
+#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE)
+static inline int _Py_IS_TYPE(PyObject *ob, PyTypeObject *type) {
+ return Py_TYPE(ob) == type;
+}
+#define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST(ob), type)
+#endif
+
+
// bpo-46906 added PyFloat_Pack2() and PyFloat_Unpack2() to Python 3.11a7.
+// bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6.0b1.
// Python 3.11a2 moved _PyFloat_Pack2() and _PyFloat_Unpack2() to the internal
// C API: Python 3.11a2-3.11a6 versions are not supported.
-#if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION)
+#if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION)
static inline int PyFloat_Pack2(double x, char *p, int le)
{ return _PyFloat_Pack2(x, (unsigned char*)p, le); }
@@ -362,6 +546,16 @@ static inline PyObject* PyCode_GetCellvars(PyCodeObject *code)
#endif
+// Py_UNUSED() was added to Python 3.4.0b2.
+#if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED)
+# if defined(__GNUC__) || defined(__clang__)
+# define Py_UNUSED(name) _unused_ ## name __attribute__((unused))
+# else
+# define Py_UNUSED(name) _unused_ ## name
+# endif
+#endif
+
+
// gh-105922 added PyImport_AddModuleRef() to Python 3.13.0a1
#if PY_VERSION_HEX < 0x030D00A0
static inline PyObject* PyImport_AddModuleRef(const char *name)
@@ -392,7 +586,96 @@ static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
return 0;
}
*pobj = Py_NewRef(obj);
- return (*pobj != NULL);
+ return 1;
+}
+#endif
+
+
+// bpo-36974 added PY_VECTORCALL_ARGUMENTS_OFFSET to Python 3.8b1
+#ifndef PY_VECTORCALL_ARGUMENTS_OFFSET
+# define PY_VECTORCALL_ARGUMENTS_OFFSET (_Py_CAST(size_t, 1) << (8 * sizeof(size_t) - 1))
+#endif
+
+// bpo-36974 added PyVectorcall_NARGS() to Python 3.8b1
+#if PY_VERSION_HEX < 0x030800B1
+static inline Py_ssize_t PyVectorcall_NARGS(size_t n)
+{
+ return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET;
+}
+#endif
+
+
+// gh-105922 added PyObject_Vectorcall() to Python 3.9.0a4
+#if PY_VERSION_HEX < 0x030900A4
+static inline PyObject*
+PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
+ size_t nargsf, PyObject *kwnames)
+{
+#if PY_VERSION_HEX >= 0x030800B1 && !defined(PYPY_VERSION)
+ // bpo-36974 added _PyObject_Vectorcall() to Python 3.8.0b1
+ return _PyObject_Vectorcall(callable, args, nargsf, kwnames);
+#else
+ PyObject *posargs = NULL, *kwargs = NULL;
+ PyObject *res;
+ Py_ssize_t nposargs, nkwargs, i;
+
+ if (nargsf != 0 && args == NULL) {
+ PyErr_BadInternalCall();
+ goto error;
+ }
+ if (kwnames != NULL && !PyTuple_Check(kwnames)) {
+ PyErr_BadInternalCall();
+ goto error;
+ }
+
+ nposargs = (Py_ssize_t)PyVectorcall_NARGS(nargsf);
+ if (kwnames) {
+ nkwargs = PyTuple_GET_SIZE(kwnames);
+ }
+ else {
+ nkwargs = 0;
+ }
+
+ posargs = PyTuple_New(nposargs);
+ if (posargs == NULL) {
+ goto error;
+ }
+ if (nposargs) {
+ for (i=0; i < nposargs; i++) {
+ PyTuple_SET_ITEM(posargs, i, Py_NewRef(*args));
+ args++;
+ }
+ }
+
+ if (nkwargs) {
+ kwargs = PyDict_New();
+ if (kwargs == NULL) {
+ goto error;
+ }
+
+ for (i = 0; i < nkwargs; i++) {
+ PyObject *key = PyTuple_GET_ITEM(kwnames, i);
+ PyObject *value = *args;
+ args++;
+ if (PyDict_SetItem(kwargs, key, value) < 0) {
+ goto error;
+ }
+ }
+ }
+ else {
+ kwargs = NULL;
+ }
+
+ res = PyObject_Call(callable, posargs, kwargs);
+ Py_DECREF(posargs);
+ Py_XDECREF(kwargs);
+ return res;
+
+error:
+ Py_DECREF(posargs);
+ Py_XDECREF(kwargs);
+ return NULL;
+#endif
}
#endif
@@ -403,7 +686,23 @@ static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
static inline int
PyObject_GetOptionalAttr(PyObject *obj, PyObject *attr_name, PyObject **result)
{
+ // bpo-32571 added _PyObject_LookupAttr() to Python 3.7.0b1
+#if PY_VERSION_HEX >= 0x030700B1 && !defined(PYPY_VERSION)
return _PyObject_LookupAttr(obj, attr_name, result);
+#else
+ *result = PyObject_GetAttr(obj, attr_name);
+ if (*result != NULL) {
+ return 1;
+ }
+ if (!PyErr_Occurred()) {
+ return 0;
+ }
+ if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ PyErr_Clear();
+ return 0;
+ }
+ return -1;
+#endif
}
static inline int
@@ -411,7 +710,11 @@ PyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, PyObject **
{
PyObject *name_obj;
int rc;
+#if PY_VERSION_HEX >= 0x03000000
name_obj = PyUnicode_FromString(attr_name);
+#else
+ name_obj = PyString_FromString(attr_name);
+#endif
if (name_obj == NULL) {
*result = NULL;
return -1;
@@ -445,7 +748,11 @@ PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **resul
{
PyObject *key_obj;
int rc;
+#if PY_VERSION_HEX >= 0x03000000
key_obj = PyUnicode_FromString(key);
+#else
+ key_obj = PyString_FromString(key);
+#endif
if (key_obj == NULL) {
*result = NULL;
return -1;
@@ -508,7 +815,11 @@ PyObject_HasAttrStringWithError(PyObject *obj, const char *attr)
static inline int
PyDict_GetItemRef(PyObject *mp, PyObject *key, PyObject **result)
{
+#if PY_VERSION_HEX >= 0x03000000
PyObject *item = PyDict_GetItemWithError(mp, key);
+#else
+ PyObject *item = _PyDict_GetItemWithError(mp, key);
+#endif
if (item != NULL) {
*result = Py_NewRef(item);
return 1; // found
@@ -525,7 +836,11 @@ static inline int
PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result)
{
int res;
+#if PY_VERSION_HEX >= 0x03000000
PyObject *key_obj = PyUnicode_FromString(key);
+#else
+ PyObject *key_obj = PyString_FromString(key);
+#endif
if (key_obj == NULL) {
*result = NULL;
return -1;
@@ -552,11 +867,16 @@ PyModule_Add(PyObject *mod, const char *name, PyObject *value)
// gh-108014 added Py_IsFinalizing() to Python 3.13.0a1
// bpo-1856 added _Py_Finalizing to Python 3.2.1b1.
// _Py_IsFinalizing() was added to PyPy 7.3.0.
-#if (PY_VERSION_HEX < 0x030D00A1) \
- && (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x7030000)
+#if (0x030201B1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030D00A1) \
+ && (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x7030000)
static inline int Py_IsFinalizing(void)
{
+#if PY_VERSION_HEX >= 0x030700A1
+ // _Py_IsFinalizing() was added to Python 3.7.0a1.
return _Py_IsFinalizing();
+#else
+ return (_Py_Finalizing != NULL);
+#endif
}
#endif
@@ -604,7 +924,7 @@ static inline int
PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
{
PyObject **dict = _PyObject_GetDictPtr(obj);
- if (*dict == NULL) {
+ if (dict == NULL || *dict == NULL) {
return -1;
}
Py_VISIT(*dict);
@@ -615,15 +935,16 @@ static inline void
PyObject_ClearManagedDict(PyObject *obj)
{
PyObject **dict = _PyObject_GetDictPtr(obj);
- if (*dict == NULL) {
+ if (dict == NULL || *dict == NULL) {
return;
}
Py_CLEAR(*dict);
}
#endif
-// gh-108867 added PyThreadState_GetUnchecked() to Python 3.13.0a1.
-#if PY_VERSION_HEX < 0x030D00A1
+// gh-108867 added PyThreadState_GetUnchecked() to Python 3.13.0a1
+// Python 3.5.2 added _PyThreadState_UncheckedGet().
+#if PY_VERSION_HEX >= 0x03050200 && PY_VERSION_HEX < 0x030D00A1
static inline PyThreadState*
PyThreadState_GetUnchecked(void)
{
@@ -645,6 +966,8 @@ PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_
// API cannot report errors so save/restore the exception
PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
+ // Python 3.3.0a1 added PyUnicode_AsUTF8AndSize()
+#if PY_VERSION_HEX >= 0x030300A1
if (PyUnicode_IS_ASCII(unicode)) {
utf8 = PyUnicode_DATA(unicode);
len = PyUnicode_GET_LENGTH(unicode);
@@ -664,6 +987,31 @@ PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_
goto done;
}
res = (memcmp(utf8, str, (size_t)len) == 0);
+#else
+ PyObject *bytes = PyUnicode_AsUTF8String(unicode);
+ if (bytes == NULL) {
+ // Memory allocation failure. The API cannot report error,
+ // so ignore the exception and return 0.
+ res = 0;
+ goto done;
+ }
+
+#if PY_VERSION_HEX >= 0x03000000
+ len = PyBytes_GET_SIZE(bytes);
+ utf8 = PyBytes_AS_STRING(bytes);
+#else
+ len = PyString_GET_SIZE(bytes);
+ utf8 = PyString_AS_STRING(bytes);
+#endif
+ if (len != str_len) {
+ Py_DECREF(bytes);
+ res = 0;
+ goto done;
+ }
+
+ res = (memcmp(utf8, str, (size_t)len) == 0);
+ Py_DECREF(bytes);
+#endif
done:
PyErr_Restore(exc_type, exc_value, exc_tb);
@@ -708,9 +1056,13 @@ PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result)
return -1;
}
+ // bpo-16991 added _PyDict_Pop() to Python 3.5.0b2.
+ // Python 3.6.0b3 changed _PyDict_Pop() first argument type to PyObject*.
// Python 3.13.0a1 removed _PyDict_Pop().
-#if defined(PYPY_VERSION) || PY_VERSION_HEX >= 0x030D0000
+#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x030500b2 || PY_VERSION_HEX >= 0x030D0000
value = PyObject_CallMethod(dict, "pop", "O", key);
+#elif PY_VERSION_HEX < 0x030600b3
+ value = _PyDict_Pop(_Py_CAST(PyDictObject*, dict), key, NULL);
#else
value = _PyDict_Pop(dict, key, NULL);
#endif
@@ -751,11 +1103,17 @@ PyDict_PopString(PyObject *dict, const char *key, PyObject **result)
#endif
+#if PY_VERSION_HEX < 0x030200A4
+// Python 3.2.0a4 added Py_hash_t type
+typedef Py_ssize_t Py_hash_t;
+#endif
+
+
// gh-111545 added Py_HashPointer() to Python 3.13.0a3
#if PY_VERSION_HEX < 0x030D00A3
static inline Py_hash_t Py_HashPointer(const void *ptr)
{
-#if !defined(PYPY_VERSION)
+#if PY_VERSION_HEX >= 0x030900A4 && !defined(PYPY_VERSION)
return _Py_HashPointer(ptr);
#else
return _Py_HashPointer(_Py_CAST(void*, ptr));
@@ -765,7 +1123,8 @@ static inline Py_hash_t Py_HashPointer(const void *ptr)
// Python 3.13a4 added a PyTime API.
-#if PY_VERSION_HEX < 0x030D00A4
+// Use the private API added to Python 3.5.
+#if PY_VERSION_HEX < 0x030D00A4 && PY_VERSION_HEX >= 0x03050000
typedef _PyTime_t PyTime_t;
#define PyTime_MIN _PyTime_MIN
#define PyTime_MAX _PyTime_MAX
@@ -781,9 +1140,9 @@ static inline int PyTime_Time(PyTime_t *result)
static inline int PyTime_PerfCounter(PyTime_t *result)
{
-#if !defined(PYPY_VERSION)
+#if PY_VERSION_HEX >= 0x03070000 && !defined(PYPY_VERSION)
return _PyTime_GetPerfCounterWithInfo(result, NULL);
-#else
+#elif PY_VERSION_HEX >= 0x03070000
// Call time.perf_counter_ns() and convert Python int object to PyTime_t.
// Cache time.perf_counter_ns() function for best performance.
static PyObject *func = NULL;
@@ -814,16 +1173,48 @@ static inline int PyTime_PerfCounter(PyTime_t *result)
Py_BUILD_ASSERT(sizeof(value) >= sizeof(PyTime_t));
*result = (PyTime_t)value;
return 0;
+#else
+ // Call time.perf_counter() and convert C double to PyTime_t.
+ // Cache time.perf_counter() function for best performance.
+ static PyObject *func = NULL;
+ if (func == NULL) {
+ PyObject *mod = PyImport_ImportModule("time");
+ if (mod == NULL) {
+ return -1;
+ }
+
+ func = PyObject_GetAttrString(mod, "perf_counter");
+ Py_DECREF(mod);
+ if (func == NULL) {
+ return -1;
+ }
+ }
+
+ PyObject *res = PyObject_CallNoArgs(func);
+ if (res == NULL) {
+ return -1;
+ }
+ double d = PyFloat_AsDouble(res);
+ Py_DECREF(res);
+
+ if (d == -1.0 && PyErr_Occurred()) {
+ return -1;
+ }
+
+ // Avoid floor() to avoid having to link to libm
+ *result = (PyTime_t)(d * 1e9);
+ return 0;
#endif
}
#endif
// gh-111389 added hash constants to Python 3.13.0a5. These constants were
-// added first as private macros to Python 3.4.0b1 and PyPy 7.3.9.
+// added first as private macros to Python 3.4.0b1 and PyPy 7.3.8.
#if (!defined(PyHASH_BITS) \
- && (!defined(PYPY_VERSION) \
- || (defined(PYPY_VERSION) && PYPY_VERSION_NUM >= 0x07090000)))
+ && ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \
+ || (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \
+ && PYPY_VERSION_NUM >= 0x07030800)))
# define PyHASH_BITS _PyHASH_BITS
# define PyHASH_MODULUS _PyHASH_MODULUS
# define PyHASH_INF _PyHASH_INF
@@ -967,7 +1358,7 @@ PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value,
# define Py_END_CRITICAL_SECTION2() }
#endif
-#if PY_VERSION_HEX < 0x030E0000 && !defined(PYPY_VERSION)
+#if PY_VERSION_HEX < 0x030E0000 && PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION)
typedef struct PyUnicodeWriter PyUnicodeWriter;
static inline void PyUnicodeWriter_Discard(PyUnicodeWriter *writer)
@@ -1135,6 +1526,678 @@ static inline int PyLong_GetSign(PyObject *obj, int *sign)
}
#endif
+// gh-126061 added PyLong_IsPositive/Negative/Zero() to Python in 3.14.0a2
+#if PY_VERSION_HEX < 0x030E00A2
+static inline int PyLong_IsPositive(PyObject *obj)
+{
+ if (!PyLong_Check(obj)) {
+ PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name);
+ return -1;
+ }
+ return _PyLong_Sign(obj) == 1;
+}
+
+static inline int PyLong_IsNegative(PyObject *obj)
+{
+ if (!PyLong_Check(obj)) {
+ PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name);
+ return -1;
+ }
+ return _PyLong_Sign(obj) == -1;
+}
+
+static inline int PyLong_IsZero(PyObject *obj)
+{
+ if (!PyLong_Check(obj)) {
+ PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name);
+ return -1;
+ }
+ return _PyLong_Sign(obj) == 0;
+}
+#endif
+
+
+// gh-124502 added PyUnicode_Equal() to Python 3.14.0a0
+#if PY_VERSION_HEX < 0x030E00A0
+static inline int PyUnicode_Equal(PyObject *str1, PyObject *str2)
+{
+ if (!PyUnicode_Check(str1)) {
+ PyErr_Format(PyExc_TypeError, "first argument must be str, not %s",
+ Py_TYPE(str1)->tp_name);
+ return -1;
+ }
+ if (!PyUnicode_Check(str2)) {
+ PyErr_Format(PyExc_TypeError, "second argument must be str, not %s",
+ Py_TYPE(str2)->tp_name);
+ return -1;
+ }
+
+#if PY_VERSION_HEX >= 0x030d0000 && !defined(PYPY_VERSION)
+ PyAPI_FUNC(int) _PyUnicode_Equal(PyObject *str1, PyObject *str2);
+
+ return _PyUnicode_Equal(str1, str2);
+#elif PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION)
+ return _PyUnicode_EQ(str1, str2);
+#elif PY_VERSION_HEX >= 0x03090000 && defined(PYPY_VERSION)
+ return _PyUnicode_EQ(str1, str2);
+#else
+ return (PyUnicode_Compare(str1, str2) == 0);
+#endif
+}
+#endif
+
+
+// gh-121645 added PyBytes_Join() to Python 3.14.0a0
+#if PY_VERSION_HEX < 0x030E00A0
+static inline PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable)
+{
+ return _PyBytes_Join(sep, iterable);
+}
+#endif
+
+
+#if PY_VERSION_HEX < 0x030E00A0
+static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len)
+{
+#if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION)
+ PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void *src, Py_ssize_t len);
+
+ return _Py_HashBytes(ptr, len);
+#else
+ Py_hash_t hash;
+ PyObject *bytes = PyBytes_FromStringAndSize((const char*)ptr, len);
+ if (bytes == NULL) {
+ return -1;
+ }
+ hash = PyObject_Hash(bytes);
+ Py_DECREF(bytes);
+ return hash;
+#endif
+}
+#endif
+
+
+#if PY_VERSION_HEX < 0x030E00A0
+static inline int PyIter_NextItem(PyObject *iter, PyObject **item)
+{
+ iternextfunc tp_iternext;
+
+ assert(iter != NULL);
+ assert(item != NULL);
+
+ tp_iternext = Py_TYPE(iter)->tp_iternext;
+ if (tp_iternext == NULL) {
+ *item = NULL;
+ PyErr_Format(PyExc_TypeError, "expected an iterator, got '%s'",
+ Py_TYPE(iter)->tp_name);
+ return -1;
+ }
+
+ if ((*item = tp_iternext(iter))) {
+ return 1;
+ }
+ if (!PyErr_Occurred()) {
+ return 0;
+ }
+ if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
+ PyErr_Clear();
+ return 0;
+ }
+ return -1;
+}
+#endif
+
+
+#if PY_VERSION_HEX < 0x030E00A0
+static inline PyObject* PyLong_FromInt32(int32_t value)
+{
+ Py_BUILD_ASSERT(sizeof(long) >= 4);
+ return PyLong_FromLong(value);
+}
+
+static inline PyObject* PyLong_FromInt64(int64_t value)
+{
+ Py_BUILD_ASSERT(sizeof(long long) >= 8);
+ return PyLong_FromLongLong(value);
+}
+
+static inline PyObject* PyLong_FromUInt32(uint32_t value)
+{
+ Py_BUILD_ASSERT(sizeof(unsigned long) >= 4);
+ return PyLong_FromUnsignedLong(value);
+}
+
+static inline PyObject* PyLong_FromUInt64(uint64_t value)
+{
+ Py_BUILD_ASSERT(sizeof(unsigned long long) >= 8);
+ return PyLong_FromUnsignedLongLong(value);
+}
+
+static inline int PyLong_AsInt32(PyObject *obj, int32_t *pvalue)
+{
+ Py_BUILD_ASSERT(sizeof(int) == 4);
+ int value = PyLong_AsInt(obj);
+ if (value == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ *pvalue = (int32_t)value;
+ return 0;
+}
+
+static inline int PyLong_AsInt64(PyObject *obj, int64_t *pvalue)
+{
+ Py_BUILD_ASSERT(sizeof(long long) == 8);
+ long long value = PyLong_AsLongLong(obj);
+ if (value == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ *pvalue = (int64_t)value;
+ return 0;
+}
+
+static inline int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue)
+{
+ Py_BUILD_ASSERT(sizeof(long) >= 4);
+ unsigned long value = PyLong_AsUnsignedLong(obj);
+ if (value == (unsigned long)-1 && PyErr_Occurred()) {
+ return -1;
+ }
+#if SIZEOF_LONG > 4
+ if ((unsigned long)UINT32_MAX < value) {
+ PyErr_SetString(PyExc_OverflowError,
+ "Python int too large to convert to C uint32_t");
+ return -1;
+ }
+#endif
+ *pvalue = (uint32_t)value;
+ return 0;
+}
+
+static inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue)
+{
+ Py_BUILD_ASSERT(sizeof(long long) == 8);
+ unsigned long long value = PyLong_AsUnsignedLongLong(obj);
+ if (value == (unsigned long long)-1 && PyErr_Occurred()) {
+ return -1;
+ }
+ *pvalue = (uint64_t)value;
+ return 0;
+}
+#endif
+
+
+// gh-102471 added import and export API for integers to 3.14.0a2.
+#if PY_VERSION_HEX < 0x030E00A2 && PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION)
+// Helpers to access PyLongObject internals.
+static inline void
+_PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size)
+{
+#if PY_VERSION_HEX >= 0x030C0000
+ op->long_value.lv_tag = (uintptr_t)(1 - sign) | ((uintptr_t)(size) << 3);
+#elif PY_VERSION_HEX >= 0x030900A4
+ Py_SET_SIZE(op, sign * size);
+#else
+ Py_SIZE(op) = sign * size;
+#endif
+}
+
+static inline Py_ssize_t
+_PyLong_DigitCount(const PyLongObject *op)
+{
+#if PY_VERSION_HEX >= 0x030C0000
+ return (Py_ssize_t)(op->long_value.lv_tag >> 3);
+#else
+ return _PyLong_Sign((PyObject*)op) < 0 ? -Py_SIZE(op) : Py_SIZE(op);
+#endif
+}
+
+static inline digit*
+_PyLong_GetDigits(const PyLongObject *op)
+{
+#if PY_VERSION_HEX >= 0x030C0000
+ return (digit*)(op->long_value.ob_digit);
+#else
+ return (digit*)(op->ob_digit);
+#endif
+}
+
+typedef struct PyLongLayout {
+ uint8_t bits_per_digit;
+ uint8_t digit_size;
+ int8_t digits_order;
+ int8_t digit_endianness;
+} PyLongLayout;
+
+typedef struct PyLongExport {
+ int64_t value;
+ uint8_t negative;
+ Py_ssize_t ndigits;
+ const void *digits;
+ Py_uintptr_t _reserved;
+} PyLongExport;
+
+typedef struct PyLongWriter PyLongWriter;
+
+static inline const PyLongLayout*
+PyLong_GetNativeLayout(void)
+{
+ static const PyLongLayout PyLong_LAYOUT = {
+ PyLong_SHIFT,
+ sizeof(digit),
+ -1, // least significant first
+ PY_LITTLE_ENDIAN ? -1 : 1,
+ };
+
+ return &PyLong_LAYOUT;
+}
+
+static inline int
+PyLong_Export(PyObject *obj, PyLongExport *export_long)
+{
+ if (!PyLong_Check(obj)) {
+ memset(export_long, 0, sizeof(*export_long));
+ PyErr_Format(PyExc_TypeError, "expected int, got %s",
+ Py_TYPE(obj)->tp_name);
+ return -1;
+ }
+
+ // Fast-path: try to convert to a int64_t
+ PyLongObject *self = (PyLongObject*)obj;
+ int overflow;
+#if SIZEOF_LONG == 8
+ long value = PyLong_AsLongAndOverflow(obj, &overflow);
+#else
+ // Windows has 32-bit long, so use 64-bit long long instead
+ long long value = PyLong_AsLongLongAndOverflow(obj, &overflow);
+#endif
+ Py_BUILD_ASSERT(sizeof(value) == sizeof(int64_t));
+ // the function cannot fail since obj is a PyLongObject
+ assert(!(value == -1 && PyErr_Occurred()));
+
+ if (!overflow) {
+ export_long->value = value;
+ export_long->negative = 0;
+ export_long->ndigits = 0;
+ export_long->digits = 0;
+ export_long->_reserved = 0;
+ }
+ else {
+ export_long->value = 0;
+ export_long->negative = _PyLong_Sign(obj) < 0;
+ export_long->ndigits = _PyLong_DigitCount(self);
+ if (export_long->ndigits == 0) {
+ export_long->ndigits = 1;
+ }
+ export_long->digits = _PyLong_GetDigits(self);
+ export_long->_reserved = (Py_uintptr_t)Py_NewRef(obj);
+ }
+ return 0;
+}
+
+static inline void
+PyLong_FreeExport(PyLongExport *export_long)
+{
+ PyObject *obj = (PyObject*)export_long->_reserved;
+
+ if (obj) {
+ export_long->_reserved = 0;
+ Py_DECREF(obj);
+ }
+}
+
+static inline PyLongWriter*
+PyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits)
+{
+ if (ndigits <= 0) {
+ PyErr_SetString(PyExc_ValueError, "ndigits must be positive");
+ return NULL;
+ }
+ assert(digits != NULL);
+
+ PyLongObject *obj = _PyLong_New(ndigits);
+ if (obj == NULL) {
+ return NULL;
+ }
+ _PyLong_SetSignAndDigitCount(obj, negative?-1:1, ndigits);
+
+ *digits = _PyLong_GetDigits(obj);
+ return (PyLongWriter*)obj;
+}
+
+static inline void
+PyLongWriter_Discard(PyLongWriter *writer)
+{
+ PyLongObject *obj = (PyLongObject *)writer;
+
+ assert(Py_REFCNT(obj) == 1);
+ Py_DECREF(obj);
+}
+
+static inline PyObject*
+PyLongWriter_Finish(PyLongWriter *writer)
+{
+ PyObject *obj = (PyObject *)writer;
+ PyLongObject *self = (PyLongObject*)obj;
+ Py_ssize_t j = _PyLong_DigitCount(self);
+ Py_ssize_t i = j;
+ int sign = _PyLong_Sign(obj);
+
+ assert(Py_REFCNT(obj) == 1);
+
+ // Normalize and get singleton if possible
+ while (i > 0 && _PyLong_GetDigits(self)[i-1] == 0) {
+ --i;
+ }
+ if (i != j) {
+ if (i == 0) {
+ sign = 0;
+ }
+ _PyLong_SetSignAndDigitCount(self, sign, i);
+ }
+ if (i <= 1) {
+ long val = sign * (long)(_PyLong_GetDigits(self)[0]);
+ Py_DECREF(obj);
+ return PyLong_FromLong(val);
+ }
+
+ return obj;
+}
+#endif
+
+
+#if PY_VERSION_HEX < 0x030C00A3
+# define Py_T_SHORT T_SHORT
+# define Py_T_INT T_INT
+# define Py_T_LONG T_LONG
+# define Py_T_FLOAT T_FLOAT
+# define Py_T_DOUBLE T_DOUBLE
+# define Py_T_STRING T_STRING
+# define _Py_T_OBJECT T_OBJECT
+# define Py_T_CHAR T_CHAR
+# define Py_T_BYTE T_BYTE
+# define Py_T_UBYTE T_UBYTE
+# define Py_T_USHORT T_USHORT
+# define Py_T_UINT T_UINT
+# define Py_T_ULONG T_ULONG
+# define Py_T_STRING_INPLACE T_STRING_INPLACE
+# define Py_T_BOOL T_BOOL
+# define Py_T_OBJECT_EX T_OBJECT_EX
+# define Py_T_LONGLONG T_LONGLONG
+# define Py_T_ULONGLONG T_ULONGLONG
+# define Py_T_PYSSIZET T_PYSSIZET
+
+# if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION)
+# define _Py_T_NONE T_NONE
+# endif
+
+# define Py_READONLY READONLY
+# define Py_AUDIT_READ READ_RESTRICTED
+# define _Py_WRITE_RESTRICTED PY_WRITE_RESTRICTED
+#endif
+
+
+// gh-127350 added Py_fopen() and Py_fclose() to Python 3.14a4
+#if PY_VERSION_HEX < 0x030E00A4
+static inline FILE* Py_fopen(PyObject *path, const char *mode)
+{
+#if 0x030400A2 <= PY_VERSION_HEX && !defined(PYPY_VERSION)
+ PyAPI_FUNC(FILE*) _Py_fopen_obj(PyObject *path, const char *mode);
+
+ return _Py_fopen_obj(path, mode);
+#else
+ FILE *f;
+ PyObject *bytes;
+#if PY_VERSION_HEX >= 0x03000000
+ if (!PyUnicode_FSConverter(path, &bytes)) {
+ return NULL;
+ }
+#else
+ if (!PyString_Check(path)) {
+ PyErr_SetString(PyExc_TypeError, "except str");
+ return NULL;
+ }
+ bytes = Py_NewRef(path);
+#endif
+ const char *path_bytes = PyBytes_AS_STRING(bytes);
+
+ f = fopen(path_bytes, mode);
+ Py_DECREF(bytes);
+
+ if (f == NULL) {
+ PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path);
+ return NULL;
+ }
+ return f;
+#endif
+}
+
+static inline int Py_fclose(FILE *file)
+{
+ return fclose(file);
+}
+#endif
+
+
+#if 0x03090000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030E0000 && !defined(PYPY_VERSION)
+static inline PyObject*
+PyConfig_Get(const char *name)
+{
+ typedef enum {
+ _PyConfig_MEMBER_INT,
+ _PyConfig_MEMBER_UINT,
+ _PyConfig_MEMBER_ULONG,
+ _PyConfig_MEMBER_BOOL,
+ _PyConfig_MEMBER_WSTR,
+ _PyConfig_MEMBER_WSTR_OPT,
+ _PyConfig_MEMBER_WSTR_LIST,
+ } PyConfigMemberType;
+
+ typedef struct {
+ const char *name;
+ size_t offset;
+ PyConfigMemberType type;
+ const char *sys_attr;
+ } PyConfigSpec;
+
+#define PYTHONCAPI_COMPAT_SPEC(MEMBER, TYPE, sys_attr) \
+ {#MEMBER, offsetof(PyConfig, MEMBER), \
+ _PyConfig_MEMBER_##TYPE, sys_attr}
+
+ static const PyConfigSpec config_spec[] = {
+ PYTHONCAPI_COMPAT_SPEC(argv, WSTR_LIST, "argv"),
+ PYTHONCAPI_COMPAT_SPEC(base_exec_prefix, WSTR_OPT, "base_exec_prefix"),
+ PYTHONCAPI_COMPAT_SPEC(base_executable, WSTR_OPT, "_base_executable"),
+ PYTHONCAPI_COMPAT_SPEC(base_prefix, WSTR_OPT, "base_prefix"),
+ PYTHONCAPI_COMPAT_SPEC(bytes_warning, UINT, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(exec_prefix, WSTR_OPT, "exec_prefix"),
+ PYTHONCAPI_COMPAT_SPEC(executable, WSTR_OPT, "executable"),
+ PYTHONCAPI_COMPAT_SPEC(inspect, BOOL, _Py_NULL),
+#if 0x030C0000 <= PY_VERSION_HEX
+ PYTHONCAPI_COMPAT_SPEC(int_max_str_digits, UINT, _Py_NULL),
+#endif
+ PYTHONCAPI_COMPAT_SPEC(interactive, BOOL, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(module_search_paths, WSTR_LIST, "path"),
+ PYTHONCAPI_COMPAT_SPEC(optimization_level, UINT, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(parser_debug, BOOL, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(platlibdir, WSTR, "platlibdir"),
+ PYTHONCAPI_COMPAT_SPEC(prefix, WSTR_OPT, "prefix"),
+ PYTHONCAPI_COMPAT_SPEC(pycache_prefix, WSTR_OPT, "pycache_prefix"),
+ PYTHONCAPI_COMPAT_SPEC(quiet, BOOL, _Py_NULL),
+#if 0x030B0000 <= PY_VERSION_HEX
+ PYTHONCAPI_COMPAT_SPEC(stdlib_dir, WSTR_OPT, "_stdlib_dir"),
+#endif
+ PYTHONCAPI_COMPAT_SPEC(use_environment, BOOL, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(verbose, UINT, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(warnoptions, WSTR_LIST, "warnoptions"),
+ PYTHONCAPI_COMPAT_SPEC(write_bytecode, BOOL, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(xoptions, WSTR_LIST, "_xoptions"),
+ PYTHONCAPI_COMPAT_SPEC(buffered_stdio, BOOL, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(check_hash_pycs_mode, WSTR, _Py_NULL),
+#if 0x030B0000 <= PY_VERSION_HEX
+ PYTHONCAPI_COMPAT_SPEC(code_debug_ranges, BOOL, _Py_NULL),
+#endif
+ PYTHONCAPI_COMPAT_SPEC(configure_c_stdio, BOOL, _Py_NULL),
+#if 0x030D0000 <= PY_VERSION_HEX
+ PYTHONCAPI_COMPAT_SPEC(cpu_count, INT, _Py_NULL),
+#endif
+ PYTHONCAPI_COMPAT_SPEC(dev_mode, BOOL, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(dump_refs, BOOL, _Py_NULL),
+#if 0x030B0000 <= PY_VERSION_HEX
+ PYTHONCAPI_COMPAT_SPEC(dump_refs_file, WSTR_OPT, _Py_NULL),
+#endif
+#ifdef Py_GIL_DISABLED
+ PYTHONCAPI_COMPAT_SPEC(enable_gil, INT, _Py_NULL),
+#endif
+ PYTHONCAPI_COMPAT_SPEC(faulthandler, BOOL, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(filesystem_encoding, WSTR, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(filesystem_errors, WSTR, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(hash_seed, ULONG, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(home, WSTR_OPT, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(import_time, BOOL, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(install_signal_handlers, BOOL, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(isolated, BOOL, _Py_NULL),
+#ifdef MS_WINDOWS
+ PYTHONCAPI_COMPAT_SPEC(legacy_windows_stdio, BOOL, _Py_NULL),
+#endif
+ PYTHONCAPI_COMPAT_SPEC(malloc_stats, BOOL, _Py_NULL),
+#if 0x030A0000 <= PY_VERSION_HEX
+ PYTHONCAPI_COMPAT_SPEC(orig_argv, WSTR_LIST, "orig_argv"),
+#endif
+ PYTHONCAPI_COMPAT_SPEC(parse_argv, BOOL, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(pathconfig_warnings, BOOL, _Py_NULL),
+#if 0x030C0000 <= PY_VERSION_HEX
+ PYTHONCAPI_COMPAT_SPEC(perf_profiling, UINT, _Py_NULL),
+#endif
+ PYTHONCAPI_COMPAT_SPEC(program_name, WSTR, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(run_command, WSTR_OPT, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(run_filename, WSTR_OPT, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(run_module, WSTR_OPT, _Py_NULL),
+#if 0x030B0000 <= PY_VERSION_HEX
+ PYTHONCAPI_COMPAT_SPEC(safe_path, BOOL, _Py_NULL),
+#endif
+ PYTHONCAPI_COMPAT_SPEC(show_ref_count, BOOL, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(site_import, BOOL, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(skip_source_first_line, BOOL, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(stdio_encoding, WSTR, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(stdio_errors, WSTR, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(tracemalloc, UINT, _Py_NULL),
+#if 0x030B0000 <= PY_VERSION_HEX
+ PYTHONCAPI_COMPAT_SPEC(use_frozen_modules, BOOL, _Py_NULL),
+#endif
+ PYTHONCAPI_COMPAT_SPEC(use_hash_seed, BOOL, _Py_NULL),
+ PYTHONCAPI_COMPAT_SPEC(user_site_directory, BOOL, _Py_NULL),
+#if 0x030A0000 <= PY_VERSION_HEX
+ PYTHONCAPI_COMPAT_SPEC(warn_default_encoding, BOOL, _Py_NULL),
+#endif
+ };
+
+#undef PYTHONCAPI_COMPAT_SPEC
+
+ const PyConfigSpec *spec;
+ int found = 0;
+ for (size_t i=0; i < sizeof(config_spec) / sizeof(config_spec[0]); i++) {
+ spec = &config_spec[i];
+ if (strcmp(spec->name, name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ if (spec->sys_attr != NULL) {
+ PyObject *value = PySys_GetObject(spec->sys_attr);
+ if (value == NULL) {
+ PyErr_Format(PyExc_RuntimeError, "lost sys.%s", spec->sys_attr);
+ return NULL;
+ }
+ return Py_NewRef(value);
+ }
+
+ PyAPI_FUNC(const PyConfig*) _Py_GetConfig(void);
+
+ const PyConfig *config = _Py_GetConfig();
+ void *member = (char *)config + spec->offset;
+ switch (spec->type) {
+ case _PyConfig_MEMBER_INT:
+ case _PyConfig_MEMBER_UINT:
+ {
+ int value = *(int *)member;
+ return PyLong_FromLong(value);
+ }
+ case _PyConfig_MEMBER_BOOL:
+ {
+ int value = *(int *)member;
+ return PyBool_FromLong(value != 0);
+ }
+ case _PyConfig_MEMBER_ULONG:
+ {
+ unsigned long value = *(unsigned long *)member;
+ return PyLong_FromUnsignedLong(value);
+ }
+ case _PyConfig_MEMBER_WSTR:
+ case _PyConfig_MEMBER_WSTR_OPT:
+ {
+ wchar_t *wstr = *(wchar_t **)member;
+ if (wstr != NULL) {
+ return PyUnicode_FromWideChar(wstr, -1);
+ }
+ else {
+ return Py_NewRef(Py_None);
+ }
+ }
+ case _PyConfig_MEMBER_WSTR_LIST:
+ {
+ const PyWideStringList *list = (const PyWideStringList *)member;
+ PyObject *tuple = PyTuple_New(list->length);
+ if (tuple == NULL) {
+ return NULL;
+ }
+
+ for (Py_ssize_t i = 0; i < list->length; i++) {
+ PyObject *item = PyUnicode_FromWideChar(list->items[i], -1);
+ if (item == NULL) {
+ Py_DECREF(tuple);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(tuple, i, item);
+ }
+ return tuple;
+ }
+ default:
+ Py_UNREACHABLE();
+ }
+ }
+
+ PyErr_Format(PyExc_ValueError, "unknown config option name: %s", name);
+ return NULL;
+}
+
+static inline int
+PyConfig_GetInt(const char *name, int *value)
+{
+ PyObject *obj = PyConfig_Get(name);
+ if (obj == NULL) {
+ return -1;
+ }
+
+ if (!PyLong_Check(obj)) {
+ Py_DECREF(obj);
+ PyErr_Format(PyExc_TypeError, "config option %s is not an int", name);
+ return -1;
+ }
+
+ int as_int = PyLong_AsInt(obj);
+ Py_DECREF(obj);
+ if (as_int == -1 && PyErr_Occurred()) {
+ PyErr_Format(PyExc_OverflowError,
+ "config option %s value does not fit into a C int", name);
+ return -1;
+ }
+
+ *value = as_int;
+ return 0;
+}
+#endif // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION)
+
#ifdef __cplusplus
}
diff --git a/contrib/python/multidict/multidict/_multilib/views.h b/contrib/python/multidict/multidict/_multilib/views.h
index ec80e07aeb0..9cf002f803b 100644
--- a/contrib/python/multidict/multidict/_multilib/views.h
+++ b/contrib/python/multidict/multidict/_multilib/views.h
@@ -9,34 +9,16 @@ 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;
+ MultiDictObject *md;
} _Multidict_ViewObject;
/********** Base **********/
static inline void
-_init_view(_Multidict_ViewObject *self, PyObject *md)
+_init_view(_Multidict_ViewObject *self, MultiDictObject *md)
{
Py_INCREF(md);
self->md = md;
@@ -67,109 +49,804 @@ multidict_view_clear(_Multidict_ViewObject *self)
static inline Py_ssize_t
multidict_view_len(_Multidict_ViewObject *self)
{
- return pair_list_len(&((MultiDictObject*)self->md)->pairs);
+ return pair_list_len(&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) {
+ int tmp;
+ Py_ssize_t self_size = PyObject_Length(self);
+ if (self_size < 0) {
return NULL;
}
- ret = PyObject_CallFunctionObjArgs(
- viewbaseset_richcmp_func, self, other, op_obj, NULL);
- Py_DECREF(op_obj);
- return ret;
+ Py_ssize_t size = PyObject_Length(other);
+ if (size < 0) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;;
+ }
+ PyObject *iter = NULL;
+ PyObject *item = NULL;
+ switch(op) {
+ case Py_LT:
+ if (self_size >= size)
+ Py_RETURN_FALSE;
+ return PyObject_RichCompare(self, other, Py_LE);
+ case Py_LE:
+ if (self_size > size) {
+ Py_RETURN_FALSE;
+ }
+ iter = PyObject_GetIter(self);
+ if (iter == NULL) {
+ goto fail;
+ }
+ while ((item = PyIter_Next(iter))) {
+ tmp = PySequence_Contains(other, item);
+ if (tmp < 0) {
+ goto fail;
+ }
+ Py_CLEAR(item);
+ if (tmp == 0) {
+ Py_CLEAR(iter);
+ Py_RETURN_FALSE;
+ }
+ }
+ Py_CLEAR(iter);
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ Py_RETURN_TRUE;
+ case Py_EQ:
+ if (self_size != size)
+ Py_RETURN_FALSE;
+ return PyObject_RichCompare(self, other, Py_LE);
+ case Py_NE:
+ tmp = PyObject_RichCompareBool(self, other, Py_EQ);
+ if (tmp < 0)
+ goto fail;
+ return PyBool_FromLong(!tmp);
+ case Py_GT:
+ if (self_size <= size)
+ Py_RETURN_FALSE;
+ return PyObject_RichCompare(self, other, Py_GE);
+ case Py_GE:
+ if (self_size < size) {
+ Py_RETURN_FALSE;
+ }
+ iter = PyObject_GetIter(other);
+ if (iter == NULL) {
+ goto fail;
+ }
+ while ((item = PyIter_Next(iter))) {
+ tmp = PySequence_Contains(self, item);
+ if (tmp < 0) {
+ goto fail;
+ }
+ Py_CLEAR(item);
+ if (tmp == 0) {
+ Py_CLEAR(iter);
+ Py_RETURN_FALSE;
+ }
+ }
+ Py_CLEAR(iter);
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ Py_RETURN_TRUE;
+ }
+fail:
+ Py_CLEAR(item);
+ Py_CLEAR(iter);
+ return NULL;
}
+
+/********** Items **********/
+
static inline PyObject *
-multidict_view_and(PyObject *self, PyObject *other)
+multidict_itemsview_new(MultiDictObject *md)
{
- return PyObject_CallFunctionObjArgs(
- viewbaseset_and_func, self, other, NULL);
+ _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_view_or(PyObject *self, PyObject *other)
+multidict_itemsview_iter(_Multidict_ViewObject *self)
{
- return PyObject_CallFunctionObjArgs(
- viewbaseset_or_func, self, other, NULL);
+ return multidict_items_iter_new(self->md);
}
static inline PyObject *
-multidict_view_sub(PyObject *self, PyObject *other)
+multidict_itemsview_repr(_Multidict_ViewObject *self)
+{
+ PyObject *name = PyObject_GetAttrString((PyObject*)Py_TYPE(self), "__name__");
+ if (name == NULL)
+ return NULL;
+ PyObject *ret = pair_list_repr(&self->md->pairs, name, true, true);
+ Py_CLEAR(name);
+ return ret;
+}
+
+static inline int
+_multidict_itemsview_parse_item(_Multidict_ViewObject *self, PyObject *arg,
+ PyObject **pidentity, PyObject **pkey,
+ PyObject **pvalue)
{
- return PyObject_CallFunctionObjArgs(
- viewbaseset_sub_func, self, other, NULL);
+ assert(pidentity != NULL);
+ if (!PyTuple_Check(arg)) {
+ return 0;
+ }
+
+ Py_ssize_t size = PyTuple_Size(arg);
+ if (size != 2) {
+ return 0;
+ }
+
+ PyObject *key = Py_NewRef(PyTuple_GET_ITEM(arg, 0));
+
+ if (pkey != NULL) {
+ *pkey = Py_NewRef(key);
+ }
+ if (pvalue != NULL) {
+ *pvalue = Py_NewRef(PyTuple_GET_ITEM(arg, 1));
+ }
+
+ *pidentity = pair_list_calc_identity(&self->md->pairs, key);
+ Py_DECREF(key);
+ if (*pidentity == NULL) {
+ if (pkey != NULL) {
+ Py_CLEAR(*pkey);
+ }
+ if (pvalue != NULL) {
+ Py_CLEAR(*pvalue);
+ }
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ return 1;
+}
+
+static int
+_set_add(PyObject *set, PyObject *key, PyObject * value)
+{
+ PyObject *tpl = PyTuple_Pack(2, key, value);
+ if (tpl == NULL) {
+ return -1;
+ }
+ int tmp = PySet_Add(set, tpl);
+ Py_DECREF(tpl);
+ return tmp;
}
static inline PyObject *
-multidict_view_xor(PyObject *self, PyObject *other)
+multidict_itemsview_and1(_Multidict_ViewObject *self, PyObject *other)
{
- return PyObject_CallFunctionObjArgs(
- viewbaseset_xor_func, self, other, NULL);
+ PyObject *identity = NULL;
+ PyObject *key = NULL;
+ PyObject *key2 = NULL;
+ PyObject *value = NULL;
+ PyObject *value2 = NULL;
+ PyObject *arg = NULL;
+ PyObject *ret = NULL;
+
+ pair_list_pos_t pos;
+
+ PyObject *iter = PyObject_GetIter(other);
+ if (iter == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ goto fail;
+ }
+ ret = PySet_New(NULL);
+ if (ret == NULL) {
+ goto fail;
+ }
+ while ((arg = PyIter_Next(iter))) {
+ int tmp = _multidict_itemsview_parse_item(self, arg,
+ &identity, &key, &value);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp == 0) {
+ Py_CLEAR(arg);
+ continue;
+ }
+
+ pair_list_init_pos(&self->md->pairs, &pos);
+
+ while (true) {
+ tmp = pair_list_next_by_identity(&self->md->pairs, &pos,
+ identity, &key2, &value2);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp == 0) {
+ break;
+ } else {
+ tmp = PyObject_RichCompareBool(value, value2, Py_EQ);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp > 0) {
+ if (_set_add(ret, key2, value2) < 0) {
+ goto fail;
+ }
+ }
+ }
+ Py_CLEAR(key2);
+ Py_CLEAR(value2);
+ }
+ Py_CLEAR(arg);
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ }
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ Py_CLEAR(iter);
+ return ret;
+fail:
+ Py_CLEAR(arg);
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ Py_CLEAR(key2);
+ Py_CLEAR(value);
+ Py_CLEAR(value2);
+ Py_CLEAR(iter);
+ Py_CLEAR(ret);
+ return 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,
-};
+static inline PyObject *
+multidict_itemsview_and2(_Multidict_ViewObject *self, PyObject *other)
+{
+ PyObject *identity = NULL;
+ PyObject *key = NULL;
+ PyObject *value = NULL;
+ PyObject *value2 = NULL;
+ PyObject *arg = NULL;
+ PyObject *ret = NULL;
-/********** Items **********/
+ pair_list_pos_t pos;
+
+ PyObject *iter = PyObject_GetIter(other);
+ if (iter == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ goto fail;
+ }
+ ret = PySet_New(NULL);
+ if (ret == NULL) {
+ goto fail;
+ }
+ while ((arg = PyIter_Next(iter))) {
+ int tmp = _multidict_itemsview_parse_item(self, arg,
+ &identity, &key, &value);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp == 0) {
+ Py_CLEAR(arg);
+ continue;
+ }
+
+ pair_list_init_pos(&self->md->pairs, &pos);
+
+ while (true) {
+ tmp = pair_list_next_by_identity(&self->md->pairs, &pos,
+ identity, NULL, &value2);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp == 0) {
+ break;
+ } else {
+ tmp = PyObject_RichCompareBool(value, value2, Py_EQ);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp > 0) {
+ if (_set_add(ret, key, value2) < 0) {
+ goto fail;
+ }
+ }
+ }
+ Py_CLEAR(value2);
+ }
+ Py_CLEAR(arg);
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ }
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ Py_CLEAR(iter);
+ return ret;
+fail:
+ Py_CLEAR(arg);
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ Py_CLEAR(value2);
+ Py_CLEAR(iter);
+ Py_CLEAR(ret);
+ return NULL;
+}
static inline PyObject *
-multidict_itemsview_new(PyObject *md)
+multidict_itemsview_and(PyObject *lft, PyObject *rht)
{
- _Multidict_ViewObject *mv = PyObject_GC_New(
- _Multidict_ViewObject, &multidict_itemsview_type);
- if (mv == NULL) {
+ int tmp = PyObject_IsInstance(lft, (PyObject *)&multidict_itemsview_type);
+ if (tmp < 0) {
return NULL;
}
+ if (tmp > 0) {
+ return multidict_itemsview_and1((_Multidict_ViewObject *)lft, rht);
+ } else {
+ tmp = PyObject_IsInstance(rht, (PyObject *)&multidict_itemsview_type);
+ if (tmp < 0) {
+ return NULL;
+ }
+ if (tmp == 0) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ return multidict_itemsview_and2((_Multidict_ViewObject *)rht, lft);
+ }
+}
- _init_view(mv, md);
+static inline PyObject *
+multidict_itemsview_or1(_Multidict_ViewObject *self, PyObject *other)
+{
+ PyObject *identity = NULL;
+ PyObject *key = NULL;
+ PyObject *value = NULL;
+ PyObject *value2 = NULL;
+ PyObject *arg = NULL;
+ PyObject *ret = NULL;
- PyObject_GC_Track(mv);
- return (PyObject *)mv;
+ pair_list_pos_t pos;
+
+ PyObject *iter = PyObject_GetIter(other);
+ if (iter == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ goto fail;
+ }
+ ret = PySet_New((PyObject *)self);
+ if (ret == NULL) {
+ goto fail;
+ }
+ while ((arg = PyIter_Next(iter))) {
+ int tmp = _multidict_itemsview_parse_item(self, arg,
+ &identity, &key, &value);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp == 0) {
+ if (PySet_Add(ret, arg) < 0) {
+ goto fail;
+ }
+ Py_CLEAR(arg);
+ continue;
+ }
+
+ pair_list_init_pos(&self->md->pairs, &pos);
+
+ while (true) {
+ tmp = pair_list_next_by_identity(&self->md->pairs, &pos,
+ identity, NULL, &value2);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp == 0) {
+ if (PySet_Add(ret, arg) < 0) {
+ goto fail;
+ }
+ break;
+ } else {
+ tmp = PyObject_RichCompareBool(value, value2, Py_EQ);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp > 0) {
+ Py_CLEAR(value2);
+ break;
+ }
+ }
+ Py_CLEAR(value2);
+ }
+ Py_CLEAR(arg);
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ }
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ Py_CLEAR(iter);
+ return ret;
+fail:
+ Py_CLEAR(arg);
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ Py_CLEAR(value2);
+ Py_CLEAR(iter);
+ Py_CLEAR(ret);
+ return NULL;
}
static inline PyObject *
-multidict_itemsview_iter(_Multidict_ViewObject *self)
+multidict_itemsview_or2(_Multidict_ViewObject *self, PyObject *other)
{
- return multidict_items_iter_new((MultiDictObject*)self->md);
+ PyObject *identity = NULL;
+ PyObject *iter = NULL;
+ PyObject *key = NULL;
+ PyObject *value = NULL;
+ PyObject *arg = NULL;
+ PyObject *tmp_set = NULL;
+
+ pair_list_pos_t pos;
+
+ PyObject *ret = PySet_New(other);
+ if (ret == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ goto fail;
+ }
+ iter = PyObject_GetIter(other);
+ if (iter == NULL) {
+ goto fail;
+ }
+ tmp_set = PySet_New(NULL);
+ if (tmp_set == NULL) {
+ goto fail;
+ }
+ while ((arg = PyIter_Next(iter))) {
+ int tmp = _multidict_itemsview_parse_item(self, arg,
+ &identity, NULL, &value);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp > 0) {
+ if (_set_add(tmp_set, identity, value) < 0) {
+ goto fail;
+ }
+ }
+ Py_CLEAR(arg);
+ }
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ Py_CLEAR(iter);
+
+ pair_list_init_pos(&self->md->pairs, &pos);
+
+ while (true) {
+ int tmp = pair_list_next(&self->md->pairs, &pos,
+ &identity, &key, &value);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp == 0) {
+ break;
+ } else {
+ PyObject *tpl = PyTuple_Pack(2, identity, value);
+ if (tpl == NULL) {
+ goto fail;
+ }
+ tmp = PySet_Contains(tmp_set, tpl);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp == 0) {
+ if (_set_add(ret, key, value) < 0) {
+ goto fail;
+ }
+ }
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ }
+ }
+ Py_CLEAR(tmp_set);
+ return ret;
+fail:
+ Py_CLEAR(arg);
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ Py_CLEAR(iter);
+ Py_CLEAR(ret);
+ Py_CLEAR(tmp_set);
+ return NULL;
}
static inline PyObject *
-multidict_itemsview_repr(_Multidict_ViewObject *self)
+multidict_itemsview_or(PyObject *lft, PyObject *rht)
{
- return PyObject_CallFunctionObjArgs(
- itemsview_repr_func, self, NULL);
+ int tmp = PyObject_IsInstance(lft, (PyObject *)&multidict_itemsview_type);
+ if (tmp < 0) {
+ return NULL;
+ }
+ if (tmp > 0) {
+ return multidict_itemsview_or1((_Multidict_ViewObject *)lft, rht);
+ } else {
+ tmp = PyObject_IsInstance(rht, (PyObject *)&multidict_itemsview_type);
+ if (tmp < 0) {
+ return NULL;
+ }
+ if (tmp == 0) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ return multidict_itemsview_or2((_Multidict_ViewObject *)rht, lft);
+ }
}
+
static inline PyObject *
-multidict_itemsview_isdisjoint(_Multidict_ViewObject *self, PyObject *other)
+multidict_itemsview_sub1(_Multidict_ViewObject *self, PyObject *other)
{
- return PyObject_CallFunctionObjArgs(
- itemsview_isdisjoint_func, self, other, NULL);
+ PyObject *arg = NULL;
+ PyObject *identity = NULL;
+ PyObject *key = NULL;
+ PyObject *value = NULL;
+ PyObject *ret = NULL;
+ PyObject *tmp_set = NULL;
+
+ pair_list_pos_t pos;
+
+ PyObject *iter = PyObject_GetIter(other);
+ if (iter == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ goto fail;
+ }
+ ret = PySet_New(NULL);
+ if (ret == NULL) {
+ goto fail;
+ }
+ tmp_set = PySet_New(NULL);
+ if (tmp_set == NULL) {
+ goto fail;
+ }
+ while ((arg = PyIter_Next(iter))) {
+ int tmp = _multidict_itemsview_parse_item(self, arg,
+ &identity, NULL, &value);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp > 0) {
+ if (_set_add(tmp_set, identity, value) < 0) {
+ goto fail;
+ }
+ }
+ Py_CLEAR(arg);
+ }
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ Py_CLEAR(iter);
+
+ pair_list_init_pos(&self->md->pairs, &pos);
+
+ while (true) {
+ int tmp = pair_list_next(&self->md->pairs, &pos,
+ &identity, &key, &value);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp == 0) {
+ break;
+ } else {
+ PyObject *tpl = PyTuple_Pack(2, identity, value);
+ if (tpl == NULL) {
+ goto fail;
+ }
+ tmp = PySet_Contains(tmp_set, tpl);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp == 0) {
+ if (_set_add(ret, key, value) < 0) {
+ goto fail;
+ }
+ }
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ }
+ }
+ Py_CLEAR(tmp_set);
+ return ret;
+fail:
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ Py_CLEAR(ret);
+ Py_CLEAR(tmp_set);
+ Py_CLEAR(iter);
+ Py_CLEAR(ret);
+ return NULL;
}
-PyDoc_STRVAR(itemsview_isdisjoint_doc,
- "Return True if two sets have a null intersection.");
+static inline PyObject *
+multidict_itemsview_sub2(_Multidict_ViewObject *self, PyObject *other)
+{
+ PyObject *arg = NULL;
+ PyObject *identity = NULL;
+ PyObject *key = NULL;
+ PyObject *value = NULL;
+ PyObject *value2 = NULL;
+ PyObject *ret = NULL;
+ PyObject *iter = PyObject_GetIter(other);
-static PyMethodDef multidict_itemsview_methods[] = {
- {
- "isdisjoint",
- (PyCFunction)multidict_itemsview_isdisjoint,
- METH_O,
- itemsview_isdisjoint_doc
- },
- {
- NULL,
- NULL
- } /* sentinel */
+ pair_list_pos_t pos;
+
+ if (iter == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ goto fail;
+ }
+ ret = PySet_New(NULL);
+ if (ret == NULL) {
+ goto fail;
+ }
+ while ((arg = PyIter_Next(iter))) {
+ int tmp = _multidict_itemsview_parse_item(self, arg,
+ &identity, NULL, &value);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp == 0) {
+ if (PySet_Add(ret, arg) < 0) {
+ goto fail;
+ }
+ Py_CLEAR(arg);
+ continue;
+ }
+
+ pair_list_init_pos(&self->md->pairs, &pos);
+
+ while (true) {
+ tmp = pair_list_next_by_identity(&self->md->pairs, &pos,
+ identity, NULL, &value2);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp == 0) {
+ if (PySet_Add(ret, arg) < 0) {
+ goto fail;
+ }
+ break;
+ } else {
+ tmp = PyObject_RichCompareBool(value, value2, Py_EQ);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp > 0) {
+ Py_CLEAR(value2);
+ break;
+ }
+ }
+ Py_CLEAR(value2);
+ }
+
+ Py_CLEAR(arg);
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ }
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ Py_CLEAR(iter);
+ return ret;
+fail:
+ Py_CLEAR(arg);
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+ Py_CLEAR(iter);
+ Py_CLEAR(ret);
+ return NULL;
+}
+
+static inline PyObject *
+multidict_itemsview_sub(PyObject *lft, PyObject *rht)
+{
+ int tmp = PyObject_IsInstance(lft, (PyObject *)&multidict_itemsview_type);
+ if (tmp < 0) {
+ return NULL;
+ }
+ if (tmp > 0) {
+ return multidict_itemsview_sub1((_Multidict_ViewObject *)lft, rht);
+ } else {
+ tmp = PyObject_IsInstance(rht, (PyObject *)&multidict_itemsview_type);
+ if (tmp < 0) {
+ return NULL;
+ }
+ if (tmp == 0) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ return multidict_itemsview_sub2((_Multidict_ViewObject *)rht, lft);
+ }
+}
+
+static inline PyObject *
+multidict_itemsview_xor(_Multidict_ViewObject *self, PyObject *other)
+{
+ int tmp = PyObject_IsInstance((PyObject *)self,
+ (PyObject *)&multidict_itemsview_type);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp == 0) {
+ tmp = PyObject_IsInstance(other, (PyObject *)&multidict_itemsview_type);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp == 0) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ return multidict_itemsview_xor((_Multidict_ViewObject *)other,
+ (PyObject *)self);
+ }
+
+ PyObject *ret = NULL;
+ PyObject *tmp1 = NULL;
+ PyObject *tmp2 = NULL;
+ PyObject *rht = PySet_New(other);
+ if (rht == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ goto fail;
+ }
+ tmp1 = PyNumber_Subtract((PyObject *)self, rht);
+ if (tmp1 == NULL) {
+ goto fail;
+ }
+ tmp2 = PyNumber_Subtract(rht, (PyObject *)self);
+ if (tmp2 == NULL) {
+ goto fail;
+ }
+ ret = PyNumber_InPlaceOr(tmp1, tmp2);
+ if (ret == NULL) {
+ goto fail;
+ }
+ Py_CLEAR(tmp1);
+ Py_CLEAR(tmp2);
+ Py_CLEAR(rht);
+ return ret;
+fail:
+ Py_CLEAR(tmp1);
+ Py_CLEAR(tmp2);
+ Py_CLEAR(rht);
+ Py_CLEAR(ret);
+ return NULL;
+}
+
+static PyNumberMethods multidict_itemsview_as_number = {
+ .nb_subtract = (binaryfunc)multidict_itemsview_sub,
+ .nb_and = (binaryfunc)multidict_itemsview_and,
+ .nb_xor = (binaryfunc)multidict_itemsview_xor,
+ .nb_or = (binaryfunc)multidict_itemsview_or,
};
static inline int
@@ -235,13 +912,90 @@ static PySequenceMethods multidict_itemsview_as_sequence = {
.sq_contains = (objobjproc)multidict_itemsview_contains,
};
+static inline PyObject *
+multidict_itemsview_isdisjoint(_Multidict_ViewObject *self, PyObject *other)
+{
+ PyObject *iter = PyObject_GetIter(other);
+ if (iter == NULL) {
+ return NULL;
+ }
+ PyObject *arg = NULL;
+ PyObject *identity = NULL;
+ PyObject *value = NULL;
+ PyObject *value2 = NULL;
+
+ pair_list_pos_t pos;
+
+ while ((arg = PyIter_Next(iter))) {
+ int tmp = _multidict_itemsview_parse_item(self, arg,
+ &identity, NULL, &value);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp == 0) {
+ Py_CLEAR(arg);
+ continue;
+ }
+
+ pair_list_init_pos(&self->md->pairs, &pos);
+
+ while (true) {
+ tmp = pair_list_next_by_identity(&self->md->pairs, &pos,
+ identity, NULL, &value2);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp == 0) {
+ Py_CLEAR(value2);
+ break;
+ } else {
+ tmp = PyObject_RichCompareBool(value, value2, Py_EQ);
+ Py_CLEAR(value2);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp > 0) {
+ Py_CLEAR(iter);
+ Py_CLEAR(arg);
+ Py_CLEAR(identity);
+ Py_CLEAR(value);
+ Py_RETURN_FALSE;
+ }
+ }
+ }
+ Py_CLEAR(arg);
+ Py_CLEAR(identity);
+ Py_CLEAR(value);
+ }
+ Py_CLEAR(iter);
+ if (PyErr_Occurred()) {
+ return NULL;
+ }
+ Py_RETURN_TRUE;
+fail:
+ Py_CLEAR(iter);
+ Py_CLEAR(arg);
+ Py_CLEAR(identity);
+ Py_CLEAR(value);
+ Py_CLEAR(value2);
+ return 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 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_number = &multidict_itemsview_as_number,
.tp_as_sequence = &multidict_itemsview_as_sequence,
.tp_getattro = PyObject_GenericGetAttr,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
@@ -256,7 +1010,7 @@ static PyTypeObject multidict_itemsview_type = {
/********** Keys **********/
static inline PyObject *
-multidict_keysview_new(PyObject *md)
+multidict_keysview_new(MultiDictObject *md)
{
_Multidict_ViewObject *mv = PyObject_GC_New(
_Multidict_ViewObject, &multidict_keysview_type);
@@ -273,43 +1027,462 @@ multidict_keysview_new(PyObject *md)
static inline PyObject *
multidict_keysview_iter(_Multidict_ViewObject *self)
{
- return multidict_keys_iter_new(((MultiDictObject*)self->md));
+ return multidict_keys_iter_new(self->md);
}
static inline PyObject *
multidict_keysview_repr(_Multidict_ViewObject *self)
{
- return PyObject_CallFunctionObjArgs(
- keysview_repr_func, self, NULL);
+ PyObject *name = PyObject_GetAttrString((PyObject*)Py_TYPE(self), "__name__");
+ if (name == NULL)
+ return NULL;
+ PyObject *ret = pair_list_repr(&self->md->pairs, name, true, false);
+ Py_CLEAR(name);
+ return ret;
}
static inline PyObject *
-multidict_keysview_isdisjoint(_Multidict_ViewObject *self, PyObject *other)
+multidict_keysview_and1(_Multidict_ViewObject *self, PyObject *other)
{
- return PyObject_CallFunctionObjArgs(
- keysview_isdisjoint_func, self, other, NULL);
+ PyObject *key = NULL;
+ PyObject *key2 = NULL;
+ PyObject *ret = NULL;
+ PyObject *iter = PyObject_GetIter(other);
+ if (iter == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ goto fail;
+ }
+ ret = PySet_New(NULL);
+ if (ret == NULL) {
+ goto fail;
+ }
+ while ((key = PyIter_Next(iter))) {
+ if (!PyUnicode_Check(key)) {
+ Py_CLEAR(key);
+ continue;
+ }
+ int tmp = pair_list_contains(&self->md->pairs, key, &key2);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp > 0) {
+ if (PySet_Add(ret, key2) < 0) {
+ goto fail;
+ }
+ }
+ Py_CLEAR(key);
+ Py_CLEAR(key2);
+ }
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ Py_CLEAR(iter);
+ return ret;
+fail:
+ Py_CLEAR(key);
+ Py_CLEAR(key2);
+ Py_CLEAR(iter);
+ Py_CLEAR(ret);
+ return NULL;
}
-PyDoc_STRVAR(keysview_isdisjoint_doc,
- "Return True if two sets have a null intersection.");
+static inline PyObject *
+multidict_keysview_and2(_Multidict_ViewObject *self, PyObject *other)
+{
+ PyObject *key = NULL;
+ PyObject *ret = NULL;
+ PyObject *iter = PyObject_GetIter(other);
+ if (iter == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ goto fail;
+ }
+ ret = PySet_New(NULL);
+ if (ret == NULL) {
+ goto fail;
+ }
+ while ((key = PyIter_Next(iter))) {
+ if (!PyUnicode_Check(key)) {
+ Py_CLEAR(key);
+ continue;
+ }
+ int tmp = pair_list_contains(&self->md->pairs, key, NULL);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp > 0) {
+ if (PySet_Add(ret, key) < 0) {
+ goto fail;
+ }
+ }
+ Py_CLEAR(key);
+ }
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ Py_CLEAR(iter);
+ return ret;
+fail:
+ Py_CLEAR(key);
+ Py_CLEAR(iter);
+ Py_CLEAR(ret);
+ return NULL;
+}
-static PyMethodDef multidict_keysview_methods[] = {
- {
- "isdisjoint",
- (PyCFunction)multidict_keysview_isdisjoint,
- METH_O,
- keysview_isdisjoint_doc
- },
- {
- NULL,
- NULL
- } /* sentinel */
+static inline PyObject *
+multidict_keysview_and(PyObject *lft, PyObject *rht)
+{
+ int tmp = PyObject_IsInstance(lft, (PyObject *)&multidict_keysview_type);
+ if (tmp < 0) {
+ return NULL;
+ }
+ if (tmp > 0) {
+ return multidict_keysview_and1((_Multidict_ViewObject *)lft, rht);
+ } else {
+ tmp = PyObject_IsInstance(rht, (PyObject *)&multidict_keysview_type);
+ if (tmp < 0) {
+ return NULL;
+ }
+ if (tmp == 0) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ return multidict_keysview_and2((_Multidict_ViewObject *)rht, lft);
+ }
+}
+
+static inline PyObject *
+multidict_keysview_or1(_Multidict_ViewObject *self, PyObject *other)
+{
+ PyObject *key = NULL;
+ PyObject *ret = NULL;
+ PyObject *iter = PyObject_GetIter(other);
+ if (iter == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ goto fail;
+ }
+ ret = PySet_New((PyObject *)self);
+ if (ret == NULL) {
+ goto fail;
+ }
+ while ((key = PyIter_Next(iter))) {
+ if (!PyUnicode_Check(key)) {
+ if (PySet_Add(ret, key) < 0) {
+ goto fail;
+ }
+ Py_CLEAR(key);
+ continue;
+ }
+ int tmp = pair_list_contains(&self->md->pairs, key, NULL);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp == 0) {
+ if (PySet_Add(ret, key) < 0) {
+ goto fail;
+ }
+ }
+ Py_CLEAR(key);
+ }
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ Py_CLEAR(iter);
+ return ret;
+fail:
+ Py_CLEAR(key);
+ Py_CLEAR(iter);
+ Py_CLEAR(ret);
+ return NULL;
+}
+
+static inline PyObject *
+multidict_keysview_or2(_Multidict_ViewObject *self, PyObject *other)
+{
+ PyObject *iter = NULL;
+ PyObject *identity = NULL;
+ PyObject *key = NULL;
+ PyObject *tmp_set = NULL;
+ PyObject *ret = PySet_New(other);
+ if (ret == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ goto fail;
+ }
+ iter = PyObject_GetIter(ret);
+ if (iter == NULL) {
+ goto fail;
+ }
+ tmp_set = PySet_New(NULL);
+ if (tmp_set == NULL) {
+ goto fail;
+ }
+ while ((key = PyIter_Next(iter))) {
+ if (!PyUnicode_Check(key)) {
+ Py_CLEAR(key);
+ continue;
+ }
+ identity = pair_list_calc_identity(&self->md->pairs, key);
+ if (identity == NULL) {
+ goto fail;
+ }
+ if (PySet_Add(tmp_set, identity) < 0) {
+ goto fail;
+ }
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ }
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ Py_CLEAR(iter);
+
+ pair_list_pos_t pos;
+ pair_list_init_pos(&self->md->pairs, &pos);
+
+ while (true) {
+ int tmp = pair_list_next(&self->md->pairs, &pos, &identity, &key, NULL);
+ if (tmp < 0) {
+ goto fail;
+ } else if (tmp == 0) {
+ break;
+ }
+
+ tmp = PySet_Contains(tmp_set, identity);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp == 0) {
+ if (PySet_Add(ret, key) < 0) {
+ goto fail;
+ }
+ }
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ }
+ Py_CLEAR(tmp_set);
+ return ret;
+fail:
+ Py_CLEAR(identity);
+ Py_CLEAR(key);
+ Py_CLEAR(iter);
+ Py_CLEAR(ret);
+ Py_CLEAR(tmp_set);
+ return NULL;
+}
+
+static inline PyObject *
+multidict_keysview_or(PyObject *lft, PyObject *rht)
+{
+ int tmp = PyObject_IsInstance(lft, (PyObject *)&multidict_keysview_type);
+ if (tmp < 0) {
+ return NULL;
+ }
+ if (tmp > 0) {
+ return multidict_keysview_or1((_Multidict_ViewObject *)lft, rht);
+ } else {
+ tmp = PyObject_IsInstance(rht, (PyObject *)&multidict_keysview_type);
+ if (tmp < 0) {
+ return NULL;
+ }
+ if (tmp == 0) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ return multidict_keysview_or2((_Multidict_ViewObject *)rht, lft);
+ }
+}
+
+static inline PyObject *
+multidict_keysview_sub1(_Multidict_ViewObject *self, PyObject *other)
+{
+ int tmp;
+ PyObject *key = NULL;
+ PyObject *key2 = NULL;
+ PyObject *ret = NULL;
+ PyObject *iter = PyObject_GetIter(other);
+ if (iter == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ goto fail;
+ }
+ ret = PySet_New((PyObject *)self);
+ if (ret == NULL) {
+ goto fail;
+ }
+ while ((key = PyIter_Next(iter))) {
+ if (!PyUnicode_Check(key)) {
+ Py_CLEAR(key);
+ continue;
+ }
+ tmp = pair_list_contains(&self->md->pairs, key, &key2);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp > 0) {
+ if (PySet_Discard(ret, key2) < 0) {
+ goto fail;
+ }
+ }
+ Py_CLEAR(key);
+ Py_CLEAR(key2);
+ }
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ Py_CLEAR(iter);
+ return ret;
+fail:
+ Py_CLEAR(key);
+ Py_CLEAR(key2);
+ Py_CLEAR(iter);
+ Py_CLEAR(ret);
+ return NULL;
+}
+
+static inline PyObject *
+multidict_keysview_sub2(_Multidict_ViewObject *self, PyObject *other)
+{
+ int tmp;
+ PyObject *key = NULL;
+ PyObject *ret = NULL;
+ PyObject *iter = PyObject_GetIter(other);
+ if (iter == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ goto fail;
+ }
+ ret = PySet_New(other);
+ if (ret == NULL) {
+ goto fail;
+ }
+ while ((key = PyIter_Next(iter))) {
+ if (!PyUnicode_Check(key)) {
+ Py_CLEAR(key);
+ continue;
+ }
+ tmp = pair_list_contains(&self->md->pairs, key, NULL);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp > 0) {
+ if (PySet_Discard(ret, key) < 0) {
+ goto fail;
+ }
+ }
+ Py_CLEAR(key);
+ }
+ if (PyErr_Occurred()) {
+ goto fail;
+ }
+ Py_CLEAR(iter);
+ return ret;
+fail:
+ Py_CLEAR(key);
+ Py_CLEAR(iter);
+ Py_CLEAR(ret);
+ return NULL;
+}
+
+static inline PyObject *
+multidict_keysview_sub(PyObject *lft, PyObject *rht)
+{
+ int tmp = PyObject_IsInstance(lft, (PyObject *)&multidict_keysview_type);
+ if (tmp < 0) {
+ return NULL;
+ }
+ if (tmp > 0) {
+ return multidict_keysview_sub1((_Multidict_ViewObject *)lft, rht);
+ } else {
+ tmp = PyObject_IsInstance(rht, (PyObject *)&multidict_keysview_type);
+ if (tmp < 0) {
+ return NULL;
+ }
+ if (tmp == 0) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ return multidict_keysview_sub2((_Multidict_ViewObject *)rht, lft);
+ }
+}
+
+static inline PyObject *
+multidict_keysview_xor(_Multidict_ViewObject *self, PyObject *other)
+{
+ int tmp = PyObject_IsInstance((PyObject *)self,
+ (PyObject *)&multidict_keysview_type);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp == 0) {
+ tmp = PyObject_IsInstance(other, (PyObject *)&multidict_keysview_type);
+ if (tmp < 0) {
+ goto fail;
+ }
+ if (tmp == 0) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ return multidict_keysview_xor((_Multidict_ViewObject *)other,
+ (PyObject *)self);
+ }
+
+ PyObject *ret = NULL;
+ PyObject *tmp1 = NULL;
+ PyObject *tmp2 = NULL;
+ PyObject *rht = PySet_New(other);
+ if (rht == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Clear();
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+ goto fail;
+ }
+ tmp1 = PyNumber_Subtract((PyObject *)self, rht);
+ if (tmp1 == NULL) {
+ goto fail;
+ }
+ tmp2 = PyNumber_Subtract(rht, (PyObject *)self);
+ if (tmp2 == NULL) {
+ goto fail;
+ }
+ ret = PyNumber_InPlaceOr(tmp1, tmp2);
+ if (ret == NULL) {
+ goto fail;
+ }
+ Py_CLEAR(tmp1);
+ Py_CLEAR(tmp2);
+ Py_CLEAR(rht);
+ return ret;
+fail:
+ Py_CLEAR(tmp1);
+ Py_CLEAR(tmp2);
+ Py_CLEAR(rht);
+ Py_CLEAR(ret);
+ return NULL;
+}
+
+static PyNumberMethods multidict_keysview_as_number = {
+ .nb_subtract = (binaryfunc)multidict_keysview_sub,
+ .nb_and = (binaryfunc)multidict_keysview_and,
+ .nb_xor = (binaryfunc)multidict_keysview_xor,
+ .nb_or = (binaryfunc)multidict_keysview_or,
};
static inline int
multidict_keysview_contains(_Multidict_ViewObject *self, PyObject *key)
{
- return pair_list_contains(&((MultiDictObject*)self->md)->pairs, key);
+ return pair_list_contains(&self->md->pairs, key, NULL);
}
static PySequenceMethods multidict_keysview_as_sequence = {
@@ -317,13 +1490,50 @@ static PySequenceMethods multidict_keysview_as_sequence = {
.sq_contains = (objobjproc)multidict_keysview_contains,
};
+static inline PyObject *
+multidict_keysview_isdisjoint(_Multidict_ViewObject *self, PyObject *other)
+{
+ PyObject *iter = PyObject_GetIter(other);
+ if (iter == NULL) {
+ return NULL;
+ }
+ PyObject *key = NULL;
+ while ((key = PyIter_Next(iter))) {
+ int tmp = pair_list_contains(&self->md->pairs, key, NULL);
+ Py_CLEAR(key);
+ if (tmp < 0) {
+ Py_CLEAR(iter);
+ return NULL;
+ }
+ if (tmp > 0) {
+ Py_CLEAR(iter);
+ Py_RETURN_FALSE;
+ }
+ }
+ Py_CLEAR(iter);
+ if (PyErr_Occurred()) {
+ return NULL;
+ }
+ Py_RETURN_TRUE;
+}
+
+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 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_number = &multidict_keysview_as_number,
.tp_as_sequence = &multidict_keysview_as_sequence,
.tp_getattro = PyObject_GenericGetAttr,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
@@ -338,7 +1548,7 @@ static PyTypeObject multidict_keysview_type = {
/********** Values **********/
static inline PyObject *
-multidict_valuesview_new(PyObject *md)
+multidict_valuesview_new(MultiDictObject *md)
{
_Multidict_ViewObject *mv = PyObject_GC_New(
_Multidict_ViewObject, &multidict_valuesview_type);
@@ -355,14 +1565,18 @@ multidict_valuesview_new(PyObject *md)
static inline PyObject *
multidict_valuesview_iter(_Multidict_ViewObject *self)
{
- return multidict_values_iter_new(((MultiDictObject*)self->md));
+ return multidict_values_iter_new(self->md);
}
static inline PyObject *
multidict_valuesview_repr(_Multidict_ViewObject *self)
{
- return PyObject_CallFunctionObjArgs(
- valuesview_repr_func, self, NULL);
+ PyObject *name = PyObject_GetAttrString((PyObject*)Py_TYPE(self), "__name__");
+ if (name == NULL)
+ return NULL;
+ PyObject *ret = pair_list_repr(&self->md->pairs, name, false, true);
+ Py_CLEAR(name);
+ return ret;
}
static PySequenceMethods multidict_valuesview_as_sequence = {
@@ -387,36 +1601,6 @@ static PyTypeObject multidict_valuesview_type = {
static inline int
multidict_views_init(void)
{
- 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_isdisjoint_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)
@@ -424,38 +1608,9 @@ multidict_views_init(void)
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
diff --git a/contrib/python/multidict/tests/gen_pickles.py b/contrib/python/multidict/tests/gen_pickles.py
index 72f41b7565f..618ce5b4d7a 100644
--- a/contrib/python/multidict/tests/gen_pickles.py
+++ b/contrib/python/multidict/tests/gen_pickles.py
@@ -3,7 +3,7 @@ from importlib import import_module
from pathlib import Path
from typing import Union
-from multidict import CIMultiDict, MultiDict
+from multidict import CIMultiDict, MultiDict, istr
TESTS_DIR = Path(__file__).parent.resolve()
_MD_Classes = Union[type[MultiDict[int]], type[CIMultiDict[int]]]
@@ -16,6 +16,13 @@ def write(tag: str, cls: _MD_Classes, proto: int) -> None:
pickle.dump(d, f, proto)
+def write_istr(tag: str, cls: type[istr], proto: int) -> None:
+ s = cls("str")
+ file_basename = f"{cls.__name__.lower()}-{tag}"
+ with (TESTS_DIR / f"{file_basename}.pickle.{proto}").open("wb") as f:
+ pickle.dump(s, f, proto)
+
+
def generate() -> None:
_impl_map = {
"c-extension": "_multidict",
@@ -26,6 +33,7 @@ def generate() -> None:
impl = import_module(f"multidict.{impl_name}")
for cls in impl.CIMultiDict, impl.MultiDict:
write(tag, cls, proto)
+ write_istr(tag, impl.istr, proto)
if __name__ == "__main__":
diff --git a/contrib/python/multidict/tests/istr-c-extension.pickle.0 b/contrib/python/multidict/tests/istr-c-extension.pickle.0
new file mode 100644
index 00000000000..2be573802a5
--- /dev/null
+++ b/contrib/python/multidict/tests/istr-c-extension.pickle.0
@@ -0,0 +1,8 @@
+cmultidict._multidict
+istr
+p0
+(Vstr
+p1
+tp2
+Rp3
+. \ No newline at end of file
diff --git a/contrib/python/multidict/tests/istr-c-extension.pickle.1 b/contrib/python/multidict/tests/istr-c-extension.pickle.1
new file mode 100644
index 00000000000..206775444b5
--- /dev/null
+++ b/contrib/python/multidict/tests/istr-c-extension.pickle.1
Binary files differ
diff --git a/contrib/python/multidict/tests/istr-c-extension.pickle.2 b/contrib/python/multidict/tests/istr-c-extension.pickle.2
new file mode 100644
index 00000000000..5c038d23faf
--- /dev/null
+++ b/contrib/python/multidict/tests/istr-c-extension.pickle.2
Binary files differ
diff --git a/contrib/python/multidict/tests/istr-c-extension.pickle.3 b/contrib/python/multidict/tests/istr-c-extension.pickle.3
new file mode 100644
index 00000000000..a9184bb4c32
--- /dev/null
+++ b/contrib/python/multidict/tests/istr-c-extension.pickle.3
Binary files differ
diff --git a/contrib/python/multidict/tests/istr-c-extension.pickle.4 b/contrib/python/multidict/tests/istr-c-extension.pickle.4
new file mode 100644
index 00000000000..d6c52d24491
--- /dev/null
+++ b/contrib/python/multidict/tests/istr-c-extension.pickle.4
Binary files differ
diff --git a/contrib/python/multidict/tests/istr-c-extension.pickle.5 b/contrib/python/multidict/tests/istr-c-extension.pickle.5
new file mode 100644
index 00000000000..fce4bc01efc
--- /dev/null
+++ b/contrib/python/multidict/tests/istr-c-extension.pickle.5
Binary files differ
diff --git a/contrib/python/multidict/tests/istr-pure-python.pickle.0 b/contrib/python/multidict/tests/istr-pure-python.pickle.0
new file mode 100644
index 00000000000..9e3f0a2a6b7
--- /dev/null
+++ b/contrib/python/multidict/tests/istr-pure-python.pickle.0
@@ -0,0 +1,14 @@
+ccopy_reg
+_reconstructor
+p0
+(cmultidict._multidict_py
+istr
+p1
+c__builtin__
+unicode
+p2
+Vstr
+p3
+tp4
+Rp5
+. \ No newline at end of file
diff --git a/contrib/python/multidict/tests/istr-pure-python.pickle.1 b/contrib/python/multidict/tests/istr-pure-python.pickle.1
new file mode 100644
index 00000000000..88b7a9d434a
--- /dev/null
+++ b/contrib/python/multidict/tests/istr-pure-python.pickle.1
Binary files differ
diff --git a/contrib/python/multidict/tests/istr-pure-python.pickle.2 b/contrib/python/multidict/tests/istr-pure-python.pickle.2
new file mode 100644
index 00000000000..9f17e5997e7
--- /dev/null
+++ b/contrib/python/multidict/tests/istr-pure-python.pickle.2
Binary files differ
diff --git a/contrib/python/multidict/tests/istr-pure-python.pickle.3 b/contrib/python/multidict/tests/istr-pure-python.pickle.3
new file mode 100644
index 00000000000..09f70c0ae81
--- /dev/null
+++ b/contrib/python/multidict/tests/istr-pure-python.pickle.3
Binary files differ
diff --git a/contrib/python/multidict/tests/istr-pure-python.pickle.4 b/contrib/python/multidict/tests/istr-pure-python.pickle.4
new file mode 100644
index 00000000000..d092a4eb550
--- /dev/null
+++ b/contrib/python/multidict/tests/istr-pure-python.pickle.4
Binary files differ
diff --git a/contrib/python/multidict/tests/istr-pure-python.pickle.5 b/contrib/python/multidict/tests/istr-pure-python.pickle.5
new file mode 100644
index 00000000000..b8f03af6562
--- /dev/null
+++ b/contrib/python/multidict/tests/istr-pure-python.pickle.5
Binary files differ
diff --git a/contrib/python/multidict/tests/test_circular_imports.py b/contrib/python/multidict/tests/test_circular_imports.py
index 00f6ae4f582..f6ea323ee22 100644
--- a/contrib/python/multidict/tests/test_circular_imports.py
+++ b/contrib/python/multidict/tests/test_circular_imports.py
@@ -51,9 +51,6 @@ def _discover_path_importables(
if pkg_dir_path.parts[-1] == "__pycache__":
continue
- if all(Path(_).suffix != ".py" for _ in file_names):
- continue
-
rel_pt = pkg_dir_path.relative_to(pkg_pth)
pkg_pref = ".".join((pkg_name,) + rel_pt.parts)
yield from (
diff --git a/contrib/python/multidict/tests/test_incorrect_args.py b/contrib/python/multidict/tests/test_incorrect_args.py
new file mode 100644
index 00000000000..280204c8d09
--- /dev/null
+++ b/contrib/python/multidict/tests/test_incorrect_args.py
@@ -0,0 +1,126 @@
+"""Test passing invalid arguments to the methods of the MultiDict class."""
+
+from dataclasses import dataclass
+from typing import cast
+
+import pytest
+
+from multidict import MultiDict
+
+
+@dataclass(frozen=True)
+class InvalidTestedMethodArgs:
+ """A set of arguments passed to methods under test."""
+
+ test_id: str
+ positional: tuple[object, ...]
+ keyword: dict[str, object]
+
+ def __str__(self) -> str:
+ """Render a test identifier as a string."""
+ return self.test_id
+
+
+ scope="module",
+ params=(
+ InvalidTestedMethodArgs("no_args", (), {}),
+ InvalidTestedMethodArgs("too_many_args", ("a", "b", "c"), {}),
+ InvalidTestedMethodArgs("wrong_kwarg", (), {"wrong": 1}),
+ InvalidTestedMethodArgs(
+ "wrong_kwarg_and_too_many_args",
+ ("a",),
+ {"wrong": 1},
+ ),
+ ),
+ ids=str,
+)
+def tested_method_args(
+ request: pytest.FixtureRequest,
+) -> InvalidTestedMethodArgs:
+ """Return an instance of a parameter set."""
+ return cast(InvalidTestedMethodArgs, request.param)
+
+
[email protected](scope="module")
+def multidict_object(
+ any_multidict_class: type[MultiDict[int]],
+) -> MultiDict[int]:
+ return any_multidict_class([("a", 1), ("a", 2)])
+
+
+def test_getall_args(
+ multidict_object: MultiDict[int],
+ tested_method_args: InvalidTestedMethodArgs,
+) -> None:
+ with pytest.raises(TypeError, match=r".*argument.*"):
+ multidict_object.getall(
+ *tested_method_args.positional,
+ **tested_method_args.keyword,
+ )
+
+
+def test_getone_args(
+ multidict_object: MultiDict[int],
+ tested_method_args: InvalidTestedMethodArgs,
+) -> None:
+ with pytest.raises(TypeError, match=r".*argument.*"):
+ multidict_object.getone(
+ *tested_method_args.positional,
+ **tested_method_args.keyword,
+ )
+
+
+def test_get_args(
+ multidict_object: MultiDict[int],
+ tested_method_args: InvalidTestedMethodArgs,
+) -> None:
+ with pytest.raises(TypeError, match=r".*argument.*"):
+ multidict_object.get(
+ *tested_method_args.positional,
+ **tested_method_args.keyword,
+ )
+
+
+def test_setdefault_args(
+ multidict_object: MultiDict[int],
+ tested_method_args: InvalidTestedMethodArgs,
+) -> None:
+ with pytest.raises(TypeError, match=r".*argument.*"):
+ multidict_object.setdefault(
+ *tested_method_args.positional,
+ **tested_method_args.keyword,
+ )
+
+
+def test_popone_args(
+ multidict_object: MultiDict[int],
+ tested_method_args: InvalidTestedMethodArgs,
+) -> None:
+ with pytest.raises(TypeError, match=r".*argument.*"):
+ multidict_object.popone(
+ *tested_method_args.positional,
+ **tested_method_args.keyword,
+ )
+
+
+def test_pop_args(
+ multidict_object: MultiDict[int],
+ tested_method_args: InvalidTestedMethodArgs,
+) -> None:
+ with pytest.raises(TypeError, match=r".*argument.*"):
+ multidict_object.pop(
+ *tested_method_args.positional,
+ **tested_method_args.keyword,
+ )
+
+
+def test_popall_args(
+ multidict_object: MultiDict[int],
+ tested_method_args: InvalidTestedMethodArgs,
+) -> None:
+ with pytest.raises(TypeError, match=r".*argument.*"):
+ multidict_object.popall(
+ *tested_method_args.positional,
+ **tested_method_args.keyword,
+ )
diff --git a/contrib/python/multidict/tests/test_istr.py b/contrib/python/multidict/tests/test_istr.py
index 101f5fe8e5d..f02a2359333 100644
--- a/contrib/python/multidict/tests/test_istr.py
+++ b/contrib/python/multidict/tests/test_istr.py
@@ -5,6 +5,7 @@ from typing import Callable, Type
import pytest
IMPLEMENTATION = getattr(sys, "implementation") # to suppress mypy error
+GIL_ENABLED = getattr(sys, "_is_gil_enabled", lambda: True)()
def test_ctor(case_insensitive_str_class: Type[str]) -> None:
@@ -63,6 +64,10 @@ def create_istrs(case_insensitive_str_class: Type[str]) -> Callable[[], None]:
IMPLEMENTATION.name != "cpython",
reason="PyPy has different GC implementation",
)
+ not GIL_ENABLED,
+ reason="free threading has different GC implementation",
+)
def test_leak(create_istrs: Callable[[], None]) -> None:
gc.collect()
cnt = len(gc.get_objects())
@@ -71,4 +76,4 @@ def test_leak(create_istrs: Callable[[], None]) -> None:
gc.collect()
cnt2 = len(gc.get_objects())
- assert abs(cnt - cnt2) < 50 # on PyPy these numbers are not equal
+ assert abs(cnt - cnt2) < 10 # on other GC impls these numbers are not equal
diff --git a/contrib/python/multidict/tests/test_multidict.py b/contrib/python/multidict/tests/test_multidict.py
index d144130a41f..48ad479deac 100644
--- a/contrib/python/multidict/tests/test_multidict.py
+++ b/contrib/python/multidict/tests/test_multidict.py
@@ -7,7 +7,7 @@ import weakref
from collections import deque
from collections.abc import Callable, Iterable, Iterator, KeysView, Mapping
from types import ModuleType
-from typing import Union, cast
+from typing import TypeVar, Union, cast
import pytest
@@ -20,6 +20,8 @@ from multidict import (
MutableMultiMapping,
)
+_T = TypeVar("_T")
+
def chained_callable(
module: ModuleType,
@@ -291,7 +293,7 @@ class BaseMultiDictTest:
self,
cls: type[MutableMultiMapping[str]],
) -> None:
- with pytest.raises(TypeError):
+ with pytest.raises(ValueError, match="multidict update sequence element"):
cls([(1, 2, 3)]) # type: ignore[call-arg]
def test_keys_is_set_less(self, cls: type[MultiDict[str]]) -> None:
@@ -752,16 +754,16 @@ class TestMultiDict(BaseMultiDictTest):
def test_items__repr__(self, cls: type[MultiDict[str]]) -> None:
d = cls([("key", "value1")], key="value2")
- expected = "_ItemsView('key': 'value1', 'key': 'value2')"
+ expected = "<_ItemsView('key': 'value1', 'key': 'value2')>"
assert repr(d.items()) == expected
def test_keys__repr__(self, cls: type[MultiDict[str]]) -> None:
d = cls([("key", "value1")], key="value2")
- assert repr(d.keys()) == "_KeysView('key', 'key')"
+ assert repr(d.keys()) == "<_KeysView('key', 'key')>"
def test_values__repr__(self, cls: type[MultiDict[str]]) -> None:
d = cls([("key", "value1")], key="value2")
- assert repr(d.values()) == "_ValuesView('value1', 'value2')"
+ assert repr(d.values()) == "<_ValuesView('value1', 'value2')>"
class TestCIMultiDict(BaseMultiDictTest):
@@ -793,6 +795,12 @@ class TestCIMultiDict(BaseMultiDictTest):
with pytest.raises(KeyError, match="key2"):
d.getone("key2")
+ def test_from_md_and_kwds(self, cls: type[CIMultiDict[str]]) -> None:
+ d = cls([("KEY", "value1")])
+ d2 = cls(d, KEY="value2")
+
+ assert list(d2.items()) == [("KEY", "value1"), ("KEY", "value2")]
+
def test_getall(self, cls: type[CIMultiDict[str]]) -> None:
d = cls([("KEY", "value1")], KEY="value2")
@@ -817,13 +825,367 @@ class TestCIMultiDict(BaseMultiDictTest):
def test_items__repr__(self, cls: type[CIMultiDict[str]]) -> None:
d = cls([("KEY", "value1")], key="value2")
- expected = "_ItemsView('KEY': 'value1', 'key': 'value2')"
+ expected = "<_ItemsView('KEY': 'value1', 'key': 'value2')>"
assert repr(d.items()) == expected
def test_keys__repr__(self, cls: type[CIMultiDict[str]]) -> None:
d = cls([("KEY", "value1")], key="value2")
- assert repr(d.keys()) == "_KeysView('KEY', 'key')"
+ assert repr(d.keys()) == "<_KeysView('KEY', 'key')>"
def test_values__repr__(self, cls: type[CIMultiDict[str]]) -> None:
d = cls([("KEY", "value1")], key="value2")
- assert repr(d.values()) == "_ValuesView('value1', 'value2')"
+ assert repr(d.values()) == "<_ValuesView('value1', 'value2')>"
+
+ def test_items_iter_of_iter(self, cls: type[CIMultiDict[str]]) -> None:
+ d = cls([("KEY", "value1")], key="value2")
+ it = iter(d.items())
+ assert iter(it) is it
+
+ def test_keys_iter_of_iter(self, cls: type[CIMultiDict[str]]) -> None:
+ d = cls([("KEY", "value1")], key="value2")
+ it = iter(d.keys())
+ assert iter(it) is it
+
+ def test_values_iter_of_iter(self, cls: type[CIMultiDict[str]]) -> None:
+ d = cls([("KEY", "value1")], key="value2")
+ it = iter(d.values())
+ assert iter(it) is it
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param({"key"}, {"KEY"}, id="ok"),
+ pytest.param({"key", 123}, {"KEY"}, id="non-str"),
+ ),
+ )
+ def test_keys_case_insensitive_and(
+ self, cls: type[CIMultiDict[str]], arg: set[_T], expected: set[_T]
+ ) -> None:
+ d = cls([("KEY", "one")])
+ assert d.keys() & arg == expected
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param(["key"], {"key"}, id="ok"),
+ pytest.param(["key", 123], {"key"}, id="non-str"),
+ ),
+ )
+ def test_keys_case_insensitive_rand(
+ self, cls: type[CIMultiDict[str]], arg: list[_T], expected: set[_T]
+ ) -> None:
+ d = cls([("KEY", "one")])
+ assert type(arg) is list
+ assert arg & d.keys() == expected
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param({"key", "other"}, {"KEY", "other"}, id="ok"),
+ pytest.param({"key", "other", 123}, {"KEY", "other", 123}, id="non-str"),
+ ),
+ )
+ def test_keys_case_insensitive_or(
+ self, cls: type[CIMultiDict[str]], arg: set[_T], expected: set[_T]
+ ) -> None:
+ d = cls([("KEY", "one")])
+
+ assert d.keys() | arg == expected
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param(["key", "other"], {"key", "other"}, id="ok"),
+ pytest.param(["key", "other", 123], {"key", "other", 123}, id="non-str"),
+ ),
+ )
+ def test_keys_case_insensitive_ror(
+ self, cls: type[CIMultiDict[str]], arg: list[_T], expected: set[_T]
+ ) -> None:
+ d = cls([("KEY", "one")])
+ assert type(arg) is list
+
+ assert arg | d.keys() == expected
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param({"key", "other"}, {"KEY2"}, id="ok"),
+ pytest.param({"key", "other", 123}, {"KEY2"}, id="non-str"),
+ ),
+ )
+ def test_keys_case_insensitive_sub(
+ self, cls: type[CIMultiDict[str]], arg: set[_T], expected: set[_T]
+ ) -> None:
+ d = cls([("KEY", "one"), ("KEY2", "two")])
+
+ assert d.keys() - arg == expected
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param(["key", "other"], {"other"}, id="ok"),
+ pytest.param(["key", "other", 123], {"other", 123}, id="non-str"),
+ ),
+ )
+ def test_keys_case_insensitive_rsub(
+ self, cls: type[CIMultiDict[str]], arg: list[_T], expected: set[_T]
+ ) -> None:
+ d = cls([("KEY", "one"), ("KEY2", "two")])
+ assert type(arg) is list
+
+ assert arg - d.keys() == expected
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param(["key", "other"], {"KEY2", "other"}, id="ok"),
+ pytest.param(["key", "other", 123], {"KEY2", "other", 123}, id="non-str"),
+ ),
+ )
+ def test_keys_case_insensitive_xor(
+ self, cls: type[CIMultiDict[str]], arg: list[_T], expected: set[_T]
+ ) -> None:
+ d = cls([("KEY", "one"), ("KEY2", "two")])
+
+ assert d.keys() ^ arg == expected
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param(["key", "other"], {"KEY2", "other"}, id="ok"),
+ pytest.param(["key", "other", 123], {"KEY2", "other", 123}, id="non-str"),
+ ),
+ )
+ def test_keys_case_insensitive_rxor(
+ self, cls: type[CIMultiDict[str]], arg: list[_T], expected: set[_T]
+ ) -> None:
+ d = cls([("KEY", "one"), ("KEY2", "two")])
+
+ assert arg ^ d.keys() == expected
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param({"key"}, False, id="ok"),
+ pytest.param({123}, True, id="non-str"),
+ ),
+ )
+ def test_keys_case_insensitive_isdisjoint(
+ self, cls: type[CIMultiDict[str]], arg: set[_T], expected: bool
+ ) -> None:
+ d = cls([("KEY", "one")])
+ assert d.keys().isdisjoint(arg) == expected
+
+ def test_keys_case_insensitive_not_iterable(
+ self, cls: type[CIMultiDict[str]]
+ ) -> None:
+ d = cls([("KEY", "one"), ("KEY2", "two")])
+
+ with pytest.raises(TypeError):
+ 123 & d.keys() # type: ignore[operator]
+
+ with pytest.raises(TypeError):
+ d.keys() & 123 # type: ignore[operator]
+
+ with pytest.raises(TypeError):
+ 123 | d.keys() # type: ignore[operator]
+
+ with pytest.raises(TypeError):
+ d.keys() | 123 # type: ignore[operator]
+
+ with pytest.raises(TypeError):
+ 123 ^ d.keys() # type: ignore[operator]
+
+ with pytest.raises(TypeError):
+ d.keys() ^ 123 # type: ignore[operator]
+
+ with pytest.raises(TypeError):
+ d.keys() - 123 # type: ignore[operator]
+
+ with pytest.raises(TypeError):
+ 123 - d.keys() # type: ignore[operator]
+
+ @pytest.mark.parametrize(
+ "param",
+ (
+ pytest.param("non-tuple", id="not-tuple"),
+ pytest.param(("key2", "two", "three"), id="not-2-elems"),
+ pytest.param((123, "two"), id="not-str"),
+ ),
+ )
+ def test_items_case_insensitive_parse_item(
+ self, cls: type[CIMultiDict[str]], param: _T
+ ) -> None:
+ d = cls([("KEY", "one")])
+ assert d.items() | {param} == {("KEY", "one"), param}
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param({("key", "one")}, {("KEY", "one")}, id="ok"),
+ pytest.param(
+ {("key", "one"), (123, "two")},
+ {("KEY", "one")},
+ id="non-str",
+ ),
+ pytest.param(
+ {("key", "one"), ("key", "two")},
+ {("KEY", "one")},
+ id="nonequal-value",
+ ),
+ ),
+ )
+ def test_items_case_insensitive_and(
+ self, cls: type[CIMultiDict[str]], arg: set[_T], expected: set[_T]
+ ) -> None:
+ d = cls([("KEY", "one")])
+ assert d.items() & arg == expected
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param([("key", "one")], {("key", "one")}, id="ok"),
+ pytest.param(
+ [("key", "one"), (123, "two")],
+ {("key", "one")},
+ id="non-str",
+ ),
+ pytest.param(
+ [("key", "one"), ("key", "two")],
+ {("key", "one")},
+ id="nonequal-value",
+ ),
+ ),
+ )
+ def test_items_case_insensitive_rand(
+ self, cls: type[CIMultiDict[str]], arg: list[_T], expected: set[_T]
+ ) -> None:
+ d = cls([("KEY", "one")])
+ assert type(arg) is list
+ assert arg & d.items() == expected
+
+ def test_items_case_insensitive_or(self, cls: type[CIMultiDict[str]]) -> None:
+ d = cls([("KEY", "one")])
+
+ assert d.items() | {("key", "one"), ("other", "two")} == {
+ ("KEY", "one"),
+ ("other", "two"),
+ }
+
+ def test_items_case_insensitive_ror(self, cls: type[CIMultiDict[str]]) -> None:
+ d = cls([("KEY", "one"), ("KEY2", "three")])
+
+ assert [("key", "one"), ("other", "two")] | d.items() == {
+ ("key", "one"),
+ ("other", "two"),
+ ("KEY2", "three"),
+ }
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param(
+ {("key", "one"), ("other", "three")}, {("KEY2", "two")}, id="ok"
+ ),
+ pytest.param(
+ {("key", "one"), (123, "three")}, {("KEY2", "two")}, id="non-str"
+ ),
+ ),
+ )
+ def test_items_case_insensitive_sub(
+ self, cls: type[CIMultiDict[str]], arg: set[_T], expected: set[_T]
+ ) -> None:
+ d = cls([("KEY", "one"), ("KEY2", "two")])
+
+ assert d.items() - arg == expected
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param(
+ [("key", "one"), ("other", "three")], {("other", "three")}, id="ok"
+ ),
+ pytest.param(
+ [("key", "one"), (123, "three")], {(123, "three")}, id="non-str"
+ ),
+ ),
+ )
+ def test_items_case_insensitive_rsub(
+ self, cls: type[CIMultiDict[str]], arg: set[_T], expected: set[_T]
+ ) -> None:
+ d = cls([("KEY", "one"), ("KEY2", "two")])
+
+ assert arg - d.items() == expected
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param(
+ {("key", "one"), ("other", "three")},
+ {("KEY2", "two"), ("other", "three")},
+ id="ok",
+ ),
+ pytest.param(
+ {("key", "one"), (123, "three")},
+ {("KEY2", "two"), (123, "three")},
+ id="non-str",
+ ),
+ ),
+ )
+ def test_items_case_insensitive_xor(
+ self, cls: type[CIMultiDict[str]], arg: set[_T], expected: set[_T]
+ ) -> None:
+ d = cls([("KEY", "one"), ("KEY2", "two")])
+
+ assert d.items() ^ arg == expected
+
+ def test_items_case_insensitive_rxor(self, cls: type[CIMultiDict[str]]) -> None:
+ d = cls([("KEY", "one"), ("KEY2", "two")])
+
+ assert [("key", "one"), ("other", "three")] ^ d.items() == {
+ ("KEY2", "two"),
+ ("other", "three"),
+ }
+
+ def test_items_case_insensitive_non_iterable(
+ self, cls: type[CIMultiDict[str]]
+ ) -> None:
+ d = cls([("KEY", "one")])
+
+ with pytest.raises(TypeError):
+ d.items() & None # type: ignore[operator]
+
+ with pytest.raises(TypeError):
+ None & d.items() # type: ignore[operator]
+
+ with pytest.raises(TypeError):
+ d.items() | None # type: ignore[operator]
+
+ with pytest.raises(TypeError):
+ None | d.items() # type: ignore[operator]
+
+ with pytest.raises(TypeError):
+ d.items() ^ None # type: ignore[operator]
+
+ with pytest.raises(TypeError):
+ None ^ d.items() # type: ignore[operator]
+
+ with pytest.raises(TypeError):
+ d.items() - None # type: ignore[operator]
+
+ with pytest.raises(TypeError):
+ None - d.items() # type: ignore[operator]
+
+ @pytest.mark.parametrize(
+ ("arg", "expected"),
+ (
+ pytest.param({("key", "one")}, False, id="ok"),
+ pytest.param({(123, "one")}, True, id="non-str"),
+ ),
+ )
+ def test_items_case_insensitive_isdisjoint(
+ self, cls: type[CIMultiDict[str]], arg: set[_T], expected: bool
+ ) -> None:
+ d = cls([("KEY", "one")])
+ assert d.items().isdisjoint(arg) == expected
diff --git a/contrib/python/multidict/tests/test_multidict_benchmarks.py b/contrib/python/multidict/tests/test_multidict_benchmarks.py
index e6a538f3ccf..bed9faa4038 100644
--- a/contrib/python/multidict/tests/test_multidict_benchmarks.py
+++ b/contrib/python/multidict/tests/test_multidict_benchmarks.py
@@ -1,10 +1,16 @@
"""codspeed benchmarks for multidict."""
-from typing import Dict, Union
+from typing import Dict, Type, Union
from pytest_codspeed import BenchmarkFixture
-from multidict import CIMultiDict, MultiDict, istr
+from multidict import (
+ CIMultiDict,
+ CIMultiDictProxy,
+ MultiDict,
+ MultiDictProxy,
+ istr,
+)
# Note that this benchmark should not be refactored to use pytest.mark.parametrize
# since each benchmark name should be unique.
@@ -12,18 +18,10 @@ from multidict import CIMultiDict, MultiDict, istr
_SENTINEL = object()
-def test_multidict_insert_str(benchmark: BenchmarkFixture) -> None:
- md: MultiDict[str] = MultiDict()
- items = [str(i) for i in range(100)]
-
- @benchmark
- def _run() -> None:
- for i in items:
- md[i] = i
-
-
-def test_cimultidict_insert_str(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[str] = CIMultiDict()
+def test_multidict_insert_str(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ md = any_multidict_class()
items = [str(i) for i in range(100)]
@benchmark
@@ -32,8 +30,11 @@ def test_cimultidict_insert_str(benchmark: BenchmarkFixture) -> None:
md[i] = i
-def test_cimultidict_insert_istr(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[istr] = CIMultiDict()
+def test_cimultidict_insert_istr(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ md = case_insensitive_multidict_class()
items = [istr(i) for i in range(100)]
@benchmark
@@ -42,49 +43,39 @@ def test_cimultidict_insert_istr(benchmark: BenchmarkFixture) -> None:
md[i] = i
-def test_multidict_add_str(benchmark: BenchmarkFixture) -> None:
- md: MultiDict[str] = MultiDict()
- items = [str(i) for i in range(100)]
-
- @benchmark
- def _run() -> None:
- for i in items:
- md.add(i, i)
-
-
-def test_cimultidict_add_str(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[str] = CIMultiDict()
+def test_multidict_add_str(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ base_md = any_multidict_class()
items = [str(i) for i in range(100)]
@benchmark
def _run() -> None:
- for i in items:
- md.add(i, i)
+ for _ in range(100):
+ md = base_md.copy()
+ for i in items:
+ md.add(i, i)
-def test_cimultidict_add_istr(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[istr] = CIMultiDict()
+def test_cimultidict_add_istr(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ base_md = case_insensitive_multidict_class()
items = [istr(i) for i in range(100)]
@benchmark
def _run() -> None:
- for i in items:
- md.add(i, i)
+ for j in range(100):
+ md = base_md.copy()
+ for i in items:
+ md.add(i, i)
-def test_multidict_pop_str(benchmark: BenchmarkFixture) -> None:
- md_base: MultiDict[str] = MultiDict((str(i), str(i)) for i in range(100))
- items = [str(i) for i in range(100)]
-
- @benchmark
- def _run() -> None:
- md = md_base.copy()
- for i in items:
- md.pop(i)
-
-
-def test_cimultidict_pop_str(benchmark: BenchmarkFixture) -> None:
- md_base: CIMultiDict[str] = CIMultiDict((str(i), str(i)) for i in range(100))
+def test_multidict_pop_str(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ md_base = any_multidict_class((str(i), str(i)) for i in range(100))
items = [str(i) for i in range(100)]
@benchmark
@@ -94,8 +85,11 @@ def test_cimultidict_pop_str(benchmark: BenchmarkFixture) -> None:
md.pop(i)
-def test_cimultidict_pop_istr(benchmark: BenchmarkFixture) -> None:
- md_base: CIMultiDict[istr] = CIMultiDict((istr(i), istr(i)) for i in range(100))
+def test_cimultidict_pop_istr(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ md_base = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(100))
items = [istr(i) for i in range(100)]
@benchmark
@@ -105,18 +99,10 @@ def test_cimultidict_pop_istr(benchmark: BenchmarkFixture) -> None:
md.pop(i)
-def test_multidict_popitem_str(benchmark: BenchmarkFixture) -> None:
- md_base: MultiDict[str] = MultiDict((str(i), str(i)) for i in range(100))
-
- @benchmark
- def _run() -> None:
- md = md_base.copy()
- for _ in range(100):
- md.popitem()
-
-
-def test_cimultidict_popitem_str(benchmark: BenchmarkFixture) -> None:
- md_base: MultiDict[str] = MultiDict((str(i), str(i)) for i in range(100))
+def test_multidict_popitem_str(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ md_base = any_multidict_class((str(i), str(i)) for i in range(100))
@benchmark
def _run() -> None:
@@ -125,89 +111,124 @@ def test_cimultidict_popitem_str(benchmark: BenchmarkFixture) -> None:
md.popitem()
-def test_multidict_clear_str(benchmark: BenchmarkFixture) -> None:
- md: MultiDict[str] = MultiDict((str(i), str(i)) for i in range(100))
+def test_multidict_clear_str(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ md = any_multidict_class((str(i), str(i)) for i in range(100))
@benchmark
def _run() -> None:
md.clear()
-def test_cimultidict_clear_str(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[str] = CIMultiDict((str(i), str(i)) for i in range(100))
+def test_multidict_update_str(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ md = any_multidict_class((str(i), str(i)) for i in range(100))
+ items = {str(i): str(i) for i in range(100, 200)}
@benchmark
def _run() -> None:
- md.clear()
+ md.update(items)
-def test_multidict_update_str(benchmark: BenchmarkFixture) -> None:
- md: MultiDict[str] = MultiDict((str(i), str(i)) for i in range(100))
- items = {str(i): str(i) for i in range(100, 200)}
+def test_cimultidict_update_istr(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ md = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(100))
+ items: Dict[Union[str, istr], istr] = {istr(i): istr(i) for i in range(100, 200)}
@benchmark
def _run() -> None:
md.update(items)
-def test_cimultidict_update_str(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[str] = CIMultiDict((str(i), str(i)) for i in range(100))
+def test_multidict_update_str_with_kwargs(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ md = any_multidict_class((str(i), str(i)) for i in range(100))
items = {str(i): str(i) for i in range(100, 200)}
+ kwargs = {str(i): str(i) for i in range(200, 300)}
@benchmark
def _run() -> None:
- md.update(items)
+ md.update(items, **kwargs)
-def test_cimultidict_update_istr(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[istr] = CIMultiDict((istr(i), istr(i)) for i in range(100))
+def test_cimultidict_update_istr_with_kwargs(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ md = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(100))
items: Dict[Union[str, istr], istr] = {istr(i): istr(i) for i in range(100, 200)}
+ kwargs = {str(i): istr(i) for i in range(200, 300)}
@benchmark
def _run() -> None:
- md.update(items)
+ md.update(items, **kwargs)
-def test_multidict_extend_str(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[str] = CIMultiDict((str(i), str(i)) for i in range(100))
+def test_multidict_extend_str(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ base_md = any_multidict_class((str(i), str(i)) for i in range(100))
items = {str(i): str(i) for i in range(200)}
@benchmark
def _run() -> None:
- md.extend(items)
+ for j in range(100):
+ md = base_md.copy()
+ md.extend(items)
-def test_cimultidict_extend_str(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[str] = CIMultiDict((str(i), str(i)) for i in range(100))
- items = {str(i): str(i) for i in range(200)}
+def test_cimultidict_extend_istr(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ base_md = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(100))
+ items = {istr(i): istr(i) for i in range(200)}
@benchmark
def _run() -> None:
- md.extend(items)
+ for _ in range(100):
+ md = base_md.copy()
+ md.extend(items)
-def test_cimultidict_extend_istr(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[istr] = CIMultiDict((istr(i), istr(i)) for i in range(100))
- items = {istr(i): istr(i) for i in range(200)}
+def test_multidict_extend_str_with_kwargs(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ base_md = any_multidict_class((str(i), str(i)) for i in range(100))
+ items = {str(i): str(i) for i in range(200)}
+ kwargs = {str(i): str(i) for i in range(200, 300)}
@benchmark
def _run() -> None:
- md.extend(items)
+ for j in range(100):
+ md = base_md.copy()
+ md.extend(items, **kwargs)
-def test_multidict_delitem_str(benchmark: BenchmarkFixture) -> None:
- md_base: MultiDict[str] = MultiDict((str(i), str(i)) for i in range(100))
- items = [str(i) for i in range(100)]
+def test_cimultidict_extend_istr_with_kwargs(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ base_md = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(100))
+ items = {istr(i): istr(i) for i in range(200)}
+ kwargs = {str(i): istr(i) for i in range(200, 300)}
@benchmark
def _run() -> None:
- md = md_base.copy()
- for i in items:
- del md[i]
+ for _ in range(100):
+ md = base_md.copy()
+ md.extend(items, **kwargs)
-def test_cimultidict_delitem_str(benchmark: BenchmarkFixture) -> None:
- md_base: CIMultiDict[str] = CIMultiDict((str(i), str(i)) for i in range(100))
+def test_multidict_delitem_str(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ md_base = any_multidict_class((str(i), str(i)) for i in range(100))
items = [str(i) for i in range(100)]
@benchmark
@@ -217,8 +238,11 @@ def test_cimultidict_delitem_str(benchmark: BenchmarkFixture) -> None:
del md[i]
-def test_cimultidict_delitem_istr(benchmark: BenchmarkFixture) -> None:
- md_base: CIMultiDict[istr] = CIMultiDict((istr(i), istr(i)) for i in range(100))
+def test_cimultidict_delitem_istr(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ md_base = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(100))
items = [istr(i) for i in range(100)]
@benchmark
@@ -228,43 +252,55 @@ def test_cimultidict_delitem_istr(benchmark: BenchmarkFixture) -> None:
del md[i]
-def test_multidict_getall_str_hit(benchmark: BenchmarkFixture) -> None:
- md: MultiDict[str] = MultiDict(("all", str(i)) for i in range(100))
+def test_multidict_getall_str_hit(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ md = any_multidict_class(("all", str(i)) for i in range(100))
@benchmark
def _run() -> None:
md.getall("all")
-def test_cimultidict_getall_str_hit(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[str] = CIMultiDict(("all", str(i)) for i in range(100))
+def test_multidict_getall_str_miss(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ md = any_multidict_class(("all", str(i)) for i in range(100))
@benchmark
def _run() -> None:
- md.getall("all")
+ md.getall("miss", ())
-def test_cimultidict_getall_istr_hit(benchmark: BenchmarkFixture) -> None:
+def test_cimultidict_getall_istr_hit(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
all_istr = istr("all")
- md: CIMultiDict[istr] = CIMultiDict((all_istr, istr(i)) for i in range(100))
+ md = case_insensitive_multidict_class((all_istr, istr(i)) for i in range(100))
@benchmark
def _run() -> None:
md.getall(all_istr)
-def test_multidict_fetch(benchmark: BenchmarkFixture) -> None:
- md: MultiDict[str] = MultiDict((str(i), str(i)) for i in range(100))
- items = [str(i) for i in range(100)]
+def test_cimultidict_getall_istr_miss(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ all_istr = istr("all")
+ miss_istr = istr("miss")
+ md = case_insensitive_multidict_class((all_istr, istr(i)) for i in range(100))
@benchmark
def _run() -> None:
- for i in items:
- md[i]
+ md.getall(miss_istr, ())
-def test_cimultidict_fetch_str(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[str] = CIMultiDict((str(i), str(i)) for i in range(100))
+def test_multidict_fetch(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ md = any_multidict_class((str(i), str(i)) for i in range(100))
items = [str(i) for i in range(100)]
@benchmark
@@ -273,8 +309,11 @@ def test_cimultidict_fetch_str(benchmark: BenchmarkFixture) -> None:
md[i]
-def test_cimultidict_fetch_istr(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[istr] = CIMultiDict((istr(i), istr(i)) for i in range(100))
+def test_cimultidict_fetch_istr(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ md = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(100))
items = [istr(i) for i in range(100)]
@benchmark
@@ -283,8 +322,10 @@ def test_cimultidict_fetch_istr(benchmark: BenchmarkFixture) -> None:
md[i]
-def test_multidict_get_hit(benchmark: BenchmarkFixture) -> None:
- md: MultiDict[str] = MultiDict((str(i), str(i)) for i in range(100))
+def test_multidict_get_hit(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ md = any_multidict_class((str(i), str(i)) for i in range(100))
items = [str(i) for i in range(100)]
@benchmark
@@ -293,8 +334,10 @@ def test_multidict_get_hit(benchmark: BenchmarkFixture) -> None:
md.get(i)
-def test_multidict_get_miss(benchmark: BenchmarkFixture) -> None:
- md: MultiDict[str] = MultiDict((str(i), str(i)) for i in range(100))
+def test_multidict_get_miss(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ md = any_multidict_class((str(i), str(i)) for i in range(100))
items = [str(i) for i in range(100, 200)]
@benchmark
@@ -303,9 +346,12 @@ def test_multidict_get_miss(benchmark: BenchmarkFixture) -> None:
md.get(i)
-def test_cimultidict_get_hit(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[str] = CIMultiDict((str(i), str(i)) for i in range(100))
- items = [str(i) for i in range(100)]
+def test_cimultidict_get_istr_hit(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ md = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(100))
+ items = [istr(i) for i in range(100)]
@benchmark
def _run() -> None:
@@ -313,9 +359,12 @@ def test_cimultidict_get_hit(benchmark: BenchmarkFixture) -> None:
md.get(i)
-def test_cimultidict_get_miss(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[str] = CIMultiDict((str(i), str(i)) for i in range(100))
- items = [str(i) for i in range(100, 200)]
+def test_cimultidict_get_istr_miss(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ md = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(100))
+ items = [istr(i) for i in range(100, 200)]
@benchmark
def _run() -> None:
@@ -323,31 +372,37 @@ def test_cimultidict_get_miss(benchmark: BenchmarkFixture) -> None:
md.get(i)
-def test_cimultidict_get_istr_hit(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[istr] = CIMultiDict((istr(i), istr(i)) for i in range(100))
- items = [istr(i) for i in range(100)]
+def test_multidict_get_hit_with_default(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ md = any_multidict_class((str(i), str(i)) for i in range(100))
+ items = [str(i) for i in range(100)]
@benchmark
def _run() -> None:
for i in items:
- md.get(i)
+ md.get(i, _SENTINEL)
-def test_cimultidict_get_istr_miss(benchmark: BenchmarkFixture) -> None:
- md: CIMultiDict[istr] = CIMultiDict((istr(i), istr(i)) for i in range(100))
- items = [istr(i) for i in range(100, 200)]
+def test_cimultidict_get_istr_hit_with_default(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ md = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(100))
+ items = [istr(i) for i in range(100)]
@benchmark
def _run() -> None:
for i in items:
- md.get(i)
+ md.get(i, _SENTINEL)
-def test_cimultidict_get_hit_with_default(
+def test_cimultidict_get_istr_with_default_miss(
benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
) -> None:
- md: CIMultiDict[str] = CIMultiDict((str(i), str(i)) for i in range(100))
- items = [str(i) for i in range(100)]
+ md = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(100))
+ items = [istr(i) for i in range(100, 200)]
@benchmark
def _run() -> None:
@@ -355,37 +410,191 @@ def test_cimultidict_get_hit_with_default(
md.get(i, _SENTINEL)
-def test_cimultidict_get_miss_with_default(
+def test_multidict_repr(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ items = [str(i) for i in range(100)]
+ md = any_multidict_class([(i, i) for i in items])
+
+ @benchmark
+ def _run() -> None:
+ repr(md)
+
+
+def test_create_empty_multidict(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ @benchmark
+ def _run() -> None:
+ any_multidict_class()
+
+
+def test_create_multidict_with_items(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ items = [(str(i), str(i)) for i in range(100)]
+
+ @benchmark
+ def _run() -> None:
+ any_multidict_class(items)
+
+
+def test_create_cimultidict_with_items_istr(
benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
) -> None:
- md: CIMultiDict[str] = CIMultiDict((str(i), str(i)) for i in range(100))
- items = [str(i) for i in range(100, 200)]
+ items = [(istr(i), istr(i)) for i in range(100)]
@benchmark
def _run() -> None:
- for i in items:
- md.get(i, _SENTINEL)
+ case_insensitive_multidict_class(items)
-def test_cimultidict_get_istr_hit_with_default(
+def test_create_multidict_with_dict(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ dct = {str(i): str(i) for i in range(100)}
+
+ @benchmark
+ def _run() -> None:
+ any_multidict_class(dct)
+
+
+def test_create_cimultidict_with_dict_istr(
benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
) -> None:
- md: CIMultiDict[istr] = CIMultiDict((istr(i), istr(i)) for i in range(100))
- items = [istr(i) for i in range(100)]
+ dct = {istr(i): istr(i) for i in range(100)}
@benchmark
def _run() -> None:
- for i in items:
- md.get(i, _SENTINEL)
+ case_insensitive_multidict_class(dct)
-def test_cimultidict_get_istr_with_default_miss(
+def test_create_multidict_with_items_with_kwargs(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ items = [(str(i), str(i)) for i in range(100)]
+ kwargs = {str(i): str(i) for i in range(100)}
+
+ @benchmark
+ def _run() -> None:
+ any_multidict_class(items, **kwargs)
+
+
+def test_create_cimultidict_with_items_istr_with_kwargs(
benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
) -> None:
- md: CIMultiDict[istr] = CIMultiDict((istr(i), istr(i)) for i in range(100))
- items = [istr(i) for i in range(100, 200)]
+ items = [(istr(i), istr(i)) for i in range(100)]
+ kwargs = {str(i): istr(i) for i in range(100)}
@benchmark
def _run() -> None:
- for i in items:
- md.get(i, _SENTINEL)
+ case_insensitive_multidict_class(items, **kwargs)
+
+
+def test_create_empty_multidictproxy(benchmark: BenchmarkFixture) -> None:
+ md: MultiDict[str] = MultiDict()
+
+ @benchmark
+ def _run() -> None:
+ MultiDictProxy(md)
+
+
+def test_create_multidictproxy(benchmark: BenchmarkFixture) -> None:
+ items = [(str(i), str(i)) for i in range(100)]
+ md: MultiDict[str] = MultiDict(items)
+
+ @benchmark
+ def _run() -> None:
+ MultiDictProxy(md)
+
+
+def test_create_empty_cimultidictproxy(
+ benchmark: BenchmarkFixture,
+) -> None:
+ md: CIMultiDict[istr] = CIMultiDict()
+
+ @benchmark
+ def _run() -> None:
+ CIMultiDictProxy(md)
+
+
+def test_create_cimultidictproxy(
+ benchmark: BenchmarkFixture,
+) -> None:
+ items = [(istr(i), istr(i)) for i in range(100)]
+ md = CIMultiDict(items)
+
+ @benchmark
+ def _run() -> None:
+ CIMultiDictProxy(md)
+
+
+def test_create_from_existing_cimultidict(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ existing = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(5))
+
+ @benchmark
+ def _run() -> None:
+ case_insensitive_multidict_class(existing)
+
+
+def test_copy_from_existing_cimultidict(
+ benchmark: BenchmarkFixture,
+ case_insensitive_multidict_class: Type[CIMultiDict[istr]],
+) -> None:
+ existing = case_insensitive_multidict_class((istr(i), istr(i)) for i in range(5))
+
+ @benchmark
+ def _run() -> None:
+ existing.copy()
+
+
+def test_iterate_multidict(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ items = [(str(i), str(i)) for i in range(100)]
+ md = any_multidict_class(items)
+
+ @benchmark
+ def _run() -> None:
+ for _ in md:
+ pass
+
+def test_iterate_multidict_keys(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ items = [(str(i), str(i)) for i in range(100)]
+ md = any_multidict_class(items)
+
+ @benchmark
+ def _run() -> None:
+ for _ in md.keys():
+ pass
+
+
+def test_iterate_multidict_values(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ items = [(str(i), str(i)) for i in range(100)]
+ md = any_multidict_class(items)
+
+ @benchmark
+ def _run() -> None:
+ for _ in md.values():
+ pass
+
+def test_iterate_multidict_items(
+ benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]
+) -> None:
+ items = [(str(i), str(i)) for i in range(100)]
+ md = any_multidict_class(items)
+
+ @benchmark
+ def _run() -> None:
+ for _, _ in md.items():
+ pass
diff --git a/contrib/python/multidict/tests/test_mutable_multidict.py b/contrib/python/multidict/tests/test_mutable_multidict.py
index 45f1cdf5f67..085999fb21c 100644
--- a/contrib/python/multidict/tests/test_mutable_multidict.py
+++ b/contrib/python/multidict/tests/test_mutable_multidict.py
@@ -158,8 +158,8 @@ class TestMutableMultiDict:
d.add("key", "val1")
d.add("key", "val2")
- assert ("key", "val1") == d.popitem()
- assert [("key", "val2")] == list(d.items())
+ assert ("key", "val2") == d.popitem()
+ assert [("key", "val1")] == list(d.items())
def test_popitem_empty_multidict(
self,
@@ -318,6 +318,38 @@ class TestMutableMultiDict:
assert {"key" + str(SIZE - 1): SIZE - 1} == d
+ def test_update(
+ self,
+ case_sensitive_multidict_class: type[CIMultiDict[Union[str, int]]],
+ ) -> None:
+ d = case_sensitive_multidict_class()
+ assert d == {}
+
+ d.update([("key", "one"), ("key", "two")], key=3, foo="bar")
+ assert d != {"key": "one", "foo": "bar"}
+ assert 4 == len(d)
+ itms = d.items()
+ # we can't guarantee order of kwargs
+ assert ("key", "one") in itms
+ assert ("key", "two") in itms
+ assert ("key", 3) in itms
+ assert ("foo", "bar") in itms
+
+ other = case_sensitive_multidict_class(bar="baz")
+ assert other == {"bar": "baz"}
+
+ d.update(other)
+ assert ("bar", "baz") in d.items()
+
+ d.update({"foo": "moo"})
+ assert ("foo", "moo") in d.items()
+
+ d.update()
+ assert 5 == len(d)
+
+ with pytest.raises(TypeError):
+ d.update("foo", "bar") # type: ignore[arg-type, call-arg]
+
class TestCIMutableMultiDict:
def test_getall(
@@ -514,9 +546,9 @@ class TestCIMutableMultiDict:
d.add("key", "val2")
pair = d.popitem()
- assert ("KEY", "val1") == pair
+ assert ("key", "val2") == pair
assert isinstance(pair[0], str)
- assert [("key", "val2")] == list(d.items())
+ assert [("KEY", "val1")] == list(d.items())
def test_popitem_empty_multidict(
self,
@@ -658,3 +690,28 @@ class TestCIMutableMultiDict:
d["c"] = "000"
# This causes an error on pypy.
list(before_mutation_values)
+
+ def test_keys_type(
+ self,
+ case_insensitive_multidict_class: type[CIMultiDict[str]],
+ case_insensitive_str_class: type[istr],
+ ) -> None:
+ d = case_insensitive_multidict_class(
+ [
+ ("KEY", "one"),
+ ]
+ )
+ d["k2"] = "2"
+ d.extend(k3="3")
+
+ for k in d:
+ assert type(k) is case_insensitive_str_class
+
+ for k in d.keys():
+ assert type(k) is case_insensitive_str_class
+
+ for k, v in d.items():
+ assert type(k) is case_insensitive_str_class
+
+ k, v = d.popitem()
+ assert type(k) is case_insensitive_str_class
diff --git a/contrib/python/multidict/tests/test_pickle.py b/contrib/python/multidict/tests/test_pickle.py
index 3159ea45c64..08b85b21f99 100644
--- a/contrib/python/multidict/tests/test_pickle.py
+++ b/contrib/python/multidict/tests/test_pickle.py
@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING
import pytest
-from multidict import MultiDict, MultiDictProxy
+from multidict import MultiDict, MultiDictProxy, istr
if TYPE_CHECKING:
from conftest import MultidictImplementation
@@ -33,6 +33,16 @@ def test_pickle_proxy(
pickle.dumps(proxy)
+def test_pickle_istr(
+ case_insensitive_str_class: type[istr], pickle_protocol: int
+) -> None:
+ s = case_insensitive_str_class("str")
+ pbytes = pickle.dumps(s, pickle_protocol)
+ obj = pickle.loads(pbytes)
+ assert s == obj
+ assert isinstance(obj, case_insensitive_str_class)
+
+
def test_load_from_file(
any_multidict_class: type[MultiDict[int]],
multidict_implementation: "MultidictImplementation",
@@ -52,3 +62,24 @@ def test_load_from_file(
obj = pickle.load(f)
assert d == obj
assert isinstance(obj, any_multidict_class)
+
+
+def test_load_istr_from_file(
+ case_insensitive_str_class: type[istr],
+ multidict_implementation: "MultidictImplementation",
+ pickle_protocol: int,
+) -> None:
+ istr_class_name = case_insensitive_str_class.__name__
+ pickle_file_basename = "-".join(
+ (
+ istr_class_name.lower(),
+ multidict_implementation.tag,
+ )
+ )
+ s = case_insensitive_str_class("str")
+ fname = f"{pickle_file_basename}.pickle.{pickle_protocol}"
+ p = here / fname
+ with p.open("rb") as f:
+ obj = pickle.load(f)
+ assert s == obj
+ assert isinstance(obj, case_insensitive_str_class)
diff --git a/contrib/python/multidict/tests/test_views_benchmarks.py b/contrib/python/multidict/tests/test_views_benchmarks.py
new file mode 100644
index 00000000000..6290b31f445
--- /dev/null
+++ b/contrib/python/multidict/tests/test_views_benchmarks.py
@@ -0,0 +1,229 @@
+"""codspeed benchmarks for multidict views."""
+
+from typing import Type
+
+from pytest_codspeed import BenchmarkFixture
+
+from multidict import MultiDict
+
+
+def test_keys_view_equals(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md1: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ md2: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+
+ @benchmark
+ def _run() -> None:
+ assert md1.keys() == md2.keys()
+
+
+def test_keys_view_not_equals(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md1: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ md2: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(20, 120)})
+
+ @benchmark
+ def _run() -> None:
+ assert md1.keys() != md2.keys()
+
+
+def test_keys_view_more(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ s = {str(i) for i in range(50)}
+
+ @benchmark
+ def _run() -> None:
+ assert md.keys() > s
+
+
+def test_keys_view_more_or_equal(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ s = {str(i) for i in range(100)}
+
+ @benchmark
+ def _run() -> None:
+ assert md.keys() >= s
+
+
+def test_keys_view_less(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ s = {str(i) for i in range(150)}
+
+ @benchmark
+ def _run() -> None:
+ assert md.keys() < s
+
+
+def test_keys_view_less_or_equal(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ s = {str(i) for i in range(100)}
+
+ @benchmark
+ def _run() -> None:
+ assert md.keys() <= s
+
+
+def test_keys_view_and(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md1: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ md2: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(50, 150)})
+
+ @benchmark
+ def _run() -> None:
+ assert len(md1.keys() & md2.keys()) == 50
+
+
+def test_keys_view_or(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md1: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ md2: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(50, 150)})
+
+ @benchmark
+ def _run() -> None:
+ assert len(md1.keys() | md2.keys()) == 150
+
+
+def test_keys_view_sub(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md1: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ md2: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(50, 150)})
+
+ @benchmark
+ def _run() -> None:
+ assert len(md1.keys() - md2.keys()) == 50
+
+
+def test_keys_view_xor(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md1: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ md2: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(50, 150)})
+
+ @benchmark
+ def _run() -> None:
+ assert len(md1.keys() ^ md2.keys()) == 100
+
+
+def test_keys_view_is_disjoint(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md1: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ md2: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100, 200)})
+
+ @benchmark
+ def _run() -> None:
+ assert md1.keys().isdisjoint(md2.keys())
+
+
+def test_keys_view_repr(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+
+ @benchmark
+ def _run() -> None:
+ repr(md.keys())
+
+
+def test_items_view_equals(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md1: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ md2: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+
+ @benchmark
+ def _run() -> None:
+ assert md1.items() == md2.items()
+
+
+def test_items_view_not_equals(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md1: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ md2: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(20, 120)})
+
+ @benchmark
+ def _run() -> None:
+ assert md1.items() != md2.items()
+
+
+def test_items_view_more(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ s = {(str(i), str(i)) for i in range(50)}
+
+ @benchmark
+ def _run() -> None:
+ assert md.items() > s
+
+
+def test_items_view_more_or_equal(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ s = {(str(i), str(i)) for i in range(100)}
+
+ @benchmark
+ def _run() -> None:
+ assert md.items() >= s
+
+
+def test_items_view_less(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ s = {(str(i), str(i)) for i in range(150)}
+
+ @benchmark
+ def _run() -> None:
+ assert md.items() < s
+
+
+def test_items_view_less_or_equal(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ s = {(str(i), str(i)) for i in range(100)}
+
+ @benchmark
+ def _run() -> None:
+ assert md.items() <= s
+
+
+def test_items_view_and(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md1: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ md2: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(50, 150)})
+
+ @benchmark
+ def _run() -> None:
+ assert len(md1.items() & md2.items()) == 50
+
+
+def test_items_view_or(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md1: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ md2: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(50, 150)})
+
+ @benchmark
+ def _run() -> None:
+ assert len(md1.items() | md2.items()) == 150
+
+
+def test_items_view_sub(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md1: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ md2: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(50, 150)})
+
+ @benchmark
+ def _run() -> None:
+ assert len(md1.items() - md2.items()) == 50
+
+
+def test_items_view_xor(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md1: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ md2: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(50, 150)})
+
+ @benchmark
+ def _run() -> None:
+ assert len(md1.items() ^ md2.items()) == 100
+
+
+def test_items_view_is_disjoint(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md1: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+ md2: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100, 200)})
+
+ @benchmark
+ def _run() -> None:
+ assert md1.items().isdisjoint(md2.items())
+
+
+def test_items_view_repr(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+
+ @benchmark
+ def _run() -> None:
+ repr(md.items())
+
+
+def test_values_view_repr(benchmark: BenchmarkFixture, any_multidict_class: Type[MultiDict[str]]) -> None:
+ md: MultiDict[str] = any_multidict_class({str(i): str(i) for i in range(100)})
+
+ @benchmark
+ def _run() -> None:
+ repr(md.values())
diff --git a/contrib/python/multidict/ya.make b/contrib/python/multidict/ya.make
index 626036249b8..ed3eec6faaf 100644
--- a/contrib/python/multidict/ya.make
+++ b/contrib/python/multidict/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(6.2.0)
+VERSION(6.3.0)
LICENSE(Apache-2.0)
@@ -27,7 +27,6 @@ PY_SRCS(
multidict/__init__.py
multidict/_abc.py
multidict/_compat.py
- multidict/_multidict_base.py
multidict/_multidict_py.py
)