Issue #25410: Cleaned up and fixed minor bugs in C implementation of OrderedDict.
This commit is contained in:
commit
3e9f87782e
|
@ -63,6 +63,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #25410: Cleaned up and fixed minor bugs in C implementation of
|
||||||
|
OrderedDict.
|
||||||
|
|
||||||
- Issue #25411: Improved Unicode support in SMTPHandler through better use of
|
- Issue #25411: Improved Unicode support in SMTPHandler through better use of
|
||||||
the email package. Thanks to user simon04 for the patch.
|
the email package. Thanks to user simon04 for the patch.
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,6 @@ For adding nodes:
|
||||||
|
|
||||||
For removing nodes:
|
For removing nodes:
|
||||||
|
|
||||||
* _odict_pop_node(od, node, key)
|
|
||||||
* _odict_clear_node(od, node)
|
* _odict_clear_node(od, node)
|
||||||
* _odict_clear_nodes(od, clear_each)
|
* _odict_clear_nodes(od, clear_each)
|
||||||
|
|
||||||
|
@ -708,18 +707,6 @@ _odict_remove_node(PyODictObject *od, _ODictNode *node)
|
||||||
od->od_state++;
|
od->od_state++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static _ODictNode *
|
|
||||||
_odict_pop_node(PyODictObject *od, _ODictNode *node, PyObject *key)
|
|
||||||
{
|
|
||||||
if (node == NULL) {
|
|
||||||
node = _odict_find_node(od, key);
|
|
||||||
if (node == NULL)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
_odict_remove_node(od, node);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If someone calls PyDict_DelItem() directly on an OrderedDict, we'll
|
/* If someone calls PyDict_DelItem() directly on an OrderedDict, we'll
|
||||||
get all sorts of problems here. In PyODict_DelItem we make sure to
|
get all sorts of problems here. In PyODict_DelItem we make sure to
|
||||||
call _odict_clear_node first.
|
call _odict_clear_node first.
|
||||||
|
@ -962,7 +949,7 @@ odict_sizeof(PyODictObject *od)
|
||||||
return NULL;
|
return NULL;
|
||||||
res += temp;
|
res += temp;
|
||||||
|
|
||||||
res += sizeof(_ODictNode) * _odict_FAST_SIZE(od); /* od_fast_nodes */
|
res += sizeof(_ODictNode *) * _odict_FAST_SIZE(od); /* od_fast_nodes */
|
||||||
if (!_odict_EMPTY(od)) {
|
if (!_odict_EMPTY(od)) {
|
||||||
res += sizeof(_ODictNode) * PyODict_SIZE(od); /* linked-list */
|
res += sizeof(_ODictNode) * PyODict_SIZE(od); /* linked-list */
|
||||||
}
|
}
|
||||||
|
@ -978,51 +965,22 @@ odict_reduce(register PyODictObject *od)
|
||||||
{
|
{
|
||||||
_Py_IDENTIFIER(__dict__);
|
_Py_IDENTIFIER(__dict__);
|
||||||
_Py_IDENTIFIER(__class__);
|
_Py_IDENTIFIER(__class__);
|
||||||
PyObject *vars = NULL, *ns = NULL, *result = NULL, *cls = NULL;
|
_Py_IDENTIFIER(items);
|
||||||
PyObject *items_iter = NULL, *items = NULL, *args = NULL;
|
PyObject *dict = NULL, *result = NULL, *cls = NULL;
|
||||||
|
PyObject *items_iter, *items, *args = NULL;
|
||||||
|
|
||||||
/* capture any instance state */
|
/* capture any instance state */
|
||||||
vars = _PyObject_GetAttrId((PyObject *)od, &PyId___dict__);
|
dict = _PyObject_GetAttrId((PyObject *)od, &PyId___dict__);
|
||||||
if (vars == NULL)
|
if (dict == NULL)
|
||||||
goto Done;
|
goto Done;
|
||||||
else {
|
else {
|
||||||
PyObject *empty, *od_vars, *iterator, *key;
|
|
||||||
Py_ssize_t ns_len;
|
|
||||||
|
|
||||||
/* od.__dict__ isn't necessarily a dict... */
|
/* od.__dict__ isn't necessarily a dict... */
|
||||||
ns = PyObject_CallMethod((PyObject *)vars, "copy", NULL);
|
Py_ssize_t dict_len = PyObject_Length(dict);
|
||||||
if (ns == NULL)
|
if (dict_len == -1)
|
||||||
goto Done;
|
goto Done;
|
||||||
empty = PyODict_New();
|
if (!dict_len) {
|
||||||
if (empty == NULL)
|
/* nothing to pickle in od.__dict__ */
|
||||||
goto Done;
|
Py_CLEAR(dict);
|
||||||
od_vars = _PyObject_GetAttrId((PyObject *)empty, &PyId___dict__);
|
|
||||||
Py_DECREF(empty);
|
|
||||||
if (od_vars == NULL)
|
|
||||||
goto Done;
|
|
||||||
iterator = PyObject_GetIter(od_vars);
|
|
||||||
Py_DECREF(od_vars);
|
|
||||||
if (iterator == NULL)
|
|
||||||
goto Done;
|
|
||||||
|
|
||||||
while ( (key = PyIter_Next(iterator)) ) {
|
|
||||||
if (PyMapping_HasKey(ns, key) && PyMapping_DelItem(ns, key) != 0) {
|
|
||||||
Py_DECREF(iterator);
|
|
||||||
Py_DECREF(key);
|
|
||||||
goto Done;
|
|
||||||
}
|
|
||||||
Py_DECREF(key);
|
|
||||||
}
|
|
||||||
Py_DECREF(iterator);
|
|
||||||
if (PyErr_Occurred())
|
|
||||||
goto Done;
|
|
||||||
|
|
||||||
ns_len = PyObject_Length(ns);
|
|
||||||
if (ns_len == -1)
|
|
||||||
goto Done;
|
|
||||||
if (!ns_len) {
|
|
||||||
/* nothing novel to pickle in od.__dict__ */
|
|
||||||
Py_CLEAR(ns);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1035,23 +993,22 @@ odict_reduce(register PyODictObject *od)
|
||||||
if (args == NULL)
|
if (args == NULL)
|
||||||
goto Done;
|
goto Done;
|
||||||
|
|
||||||
items = PyObject_CallMethod((PyObject *)od, "items", NULL);
|
items = _PyObject_CallMethodIdObjArgs((PyObject *)od, &PyId_items, NULL);
|
||||||
if (items == NULL)
|
if (items == NULL)
|
||||||
goto Done;
|
goto Done;
|
||||||
|
|
||||||
items_iter = PyObject_GetIter(items);
|
items_iter = PyObject_GetIter(items);
|
||||||
|
Py_DECREF(items);
|
||||||
if (items_iter == NULL)
|
if (items_iter == NULL)
|
||||||
goto Done;
|
goto Done;
|
||||||
|
|
||||||
result = PyTuple_Pack(5, cls, args, ns ? ns : Py_None, Py_None, items_iter);
|
result = PyTuple_Pack(5, cls, args, dict ? dict : Py_None, Py_None, items_iter);
|
||||||
|
Py_DECREF(items_iter);
|
||||||
|
|
||||||
Done:
|
Done:
|
||||||
Py_XDECREF(vars);
|
Py_XDECREF(dict);
|
||||||
Py_XDECREF(ns);
|
|
||||||
Py_XDECREF(cls);
|
Py_XDECREF(cls);
|
||||||
Py_XDECREF(args);
|
Py_XDECREF(args);
|
||||||
Py_XDECREF(items);
|
|
||||||
Py_XDECREF(items_iter);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1203,35 +1160,24 @@ static PyObject *
|
||||||
odict_popitem(PyObject *od, PyObject *args, PyObject *kwargs)
|
odict_popitem(PyObject *od, PyObject *args, PyObject *kwargs)
|
||||||
{
|
{
|
||||||
static char *kwlist[] = {"last", 0};
|
static char *kwlist[] = {"last", 0};
|
||||||
PyObject *key, *value, *item = NULL, *last = NULL;
|
PyObject *key, *value, *item = NULL;
|
||||||
_ODictNode *node;
|
_ODictNode *node;
|
||||||
int pos = -1;
|
int last = 1;
|
||||||
|
|
||||||
if (_odict_EMPTY((PyODictObject *)od)) {
|
|
||||||
PyErr_SetString(PyExc_KeyError, "dictionary is empty");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* pull the item */
|
/* pull the item */
|
||||||
|
|
||||||
/* borrowed */
|
/* borrowed */
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:popitem", kwlist,
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|p:popitem", kwlist,
|
||||||
&last)) {
|
&last)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last != NULL) {
|
if (_odict_EMPTY(od)) {
|
||||||
int is_true;
|
PyErr_SetString(PyExc_KeyError, "dictionary is empty");
|
||||||
is_true = PyObject_IsTrue(last);
|
|
||||||
if (is_true == -1)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
pos = is_true ? -1 : 0;
|
|
||||||
}
|
}
|
||||||
if (pos == 0)
|
|
||||||
node = _odict_FIRST((PyODictObject *)od);
|
|
||||||
else
|
|
||||||
node = _odict_LAST((PyODictObject *)od);
|
|
||||||
|
|
||||||
|
node = last ? _odict_LAST(od) : _odict_FIRST(od);
|
||||||
key = _odictnode_KEY(node);
|
key = _odictnode_KEY(node);
|
||||||
Py_INCREF(key);
|
Py_INCREF(key);
|
||||||
value = _odict_popkey(od, key, NULL);
|
value = _odict_popkey(od, key, NULL);
|
||||||
|
@ -1373,58 +1319,39 @@ static PyObject *
|
||||||
odict_move_to_end(PyODictObject *od, PyObject *args, PyObject *kwargs)
|
odict_move_to_end(PyODictObject *od, PyObject *args, PyObject *kwargs)
|
||||||
{
|
{
|
||||||
static char *kwlist[] = {"key", "last", 0};
|
static char *kwlist[] = {"key", "last", 0};
|
||||||
PyObject *key, *last = NULL;
|
PyObject *key;
|
||||||
Py_ssize_t pos = -1;
|
int last = 1;
|
||||||
|
_ODictNode *node;
|
||||||
|
|
||||||
/* both borrowed */
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|p:move_to_end", kwlist,
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:move_to_end", kwlist,
|
|
||||||
&key, &last)) {
|
&key, &last)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_odict_EMPTY(od)) {
|
if (_odict_EMPTY(od)) {
|
||||||
PyErr_SetObject(PyExc_KeyError, key);
|
PyErr_SetObject(PyExc_KeyError, key);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (last != NULL) {
|
node = last ? _odict_LAST(od) : _odict_FIRST(od);
|
||||||
int is_true;
|
if (key != _odictnode_KEY(node)) {
|
||||||
is_true = PyObject_IsTrue(last);
|
node = _odict_find_node(od, key);
|
||||||
if (is_true == -1)
|
if (node == NULL) {
|
||||||
return NULL;
|
|
||||||
pos = is_true ? -1 : 0;
|
|
||||||
}
|
|
||||||
if (pos == 0) {
|
|
||||||
/* Only move if not already the first one. */
|
|
||||||
PyObject *first_key = _odictnode_KEY(_odict_FIRST(od));
|
|
||||||
int not_equal = PyObject_RichCompareBool(key, first_key, Py_NE);
|
|
||||||
if (not_equal == -1)
|
|
||||||
return NULL;
|
|
||||||
if (not_equal) {
|
|
||||||
_ODictNode *node = _odict_pop_node(od, NULL, key);
|
|
||||||
if (node != NULL) {
|
|
||||||
_odict_add_head(od, node);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!PyErr_Occurred())
|
if (!PyErr_Occurred())
|
||||||
PyErr_SetObject(PyExc_KeyError, key);
|
PyErr_SetObject(PyExc_KeyError, key);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
if (last) {
|
||||||
}
|
|
||||||
else if (pos == -1) {
|
|
||||||
/* Only move if not already the last one. */
|
/* Only move if not already the last one. */
|
||||||
PyObject *last_key = _odictnode_KEY(_odict_LAST(od));
|
if (node != _odict_LAST(od)) {
|
||||||
int not_equal = PyObject_RichCompareBool(key, last_key, Py_NE);
|
_odict_remove_node(od, node);
|
||||||
if (not_equal == -1)
|
|
||||||
return NULL;
|
|
||||||
if (not_equal) {
|
|
||||||
_ODictNode *node = _odict_pop_node(od, NULL, key);
|
|
||||||
if (node != NULL) {
|
|
||||||
_odict_add_tail(od, node);
|
_odict_add_tail(od, node);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
if (!PyErr_Occurred())
|
/* Only move if not already the first one. */
|
||||||
PyErr_SetObject(PyExc_KeyError, key);
|
if (node != _odict_FIRST(od)) {
|
||||||
return NULL;
|
_odict_remove_node(od, node);
|
||||||
|
_odict_add_head(od, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1529,12 +1456,12 @@ static PyObject *
|
||||||
odict_repr(PyODictObject *self)
|
odict_repr(PyODictObject *self)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
const char *formatstr;
|
|
||||||
_Py_IDENTIFIER(__class__);
|
_Py_IDENTIFIER(__class__);
|
||||||
_Py_IDENTIFIER(__name__);
|
_Py_IDENTIFIER(__name__);
|
||||||
|
_Py_IDENTIFIER(items);
|
||||||
Py_ssize_t count = -1;
|
Py_ssize_t count = -1;
|
||||||
PyObject *pieces = NULL, *result = NULL, *cls = NULL;
|
PyObject *pieces = NULL, *result = NULL, *cls = NULL;
|
||||||
PyObject *classname = NULL, *format = NULL, *args = NULL;
|
PyObject *classname = NULL;
|
||||||
|
|
||||||
i = Py_ReprEnter((PyObject *)self);
|
i = Py_ReprEnter((PyObject *)self);
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
|
@ -1569,7 +1496,8 @@ odict_repr(PyODictObject *self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PyObject *items = PyObject_CallMethod((PyObject *)self, "items", NULL);
|
PyObject *items = _PyObject_CallMethodIdObjArgs((PyObject *)self,
|
||||||
|
&PyId_items, NULL);
|
||||||
if (items == NULL)
|
if (items == NULL)
|
||||||
goto Done;
|
goto Done;
|
||||||
pieces = PySequence_List(items);
|
pieces = PySequence_List(items);
|
||||||
|
@ -1586,28 +1514,15 @@ Finish:
|
||||||
if (classname == NULL)
|
if (classname == NULL)
|
||||||
goto Done;
|
goto Done;
|
||||||
|
|
||||||
if (pieces == NULL) {
|
if (pieces == NULL)
|
||||||
formatstr = "%s()";
|
result = PyUnicode_FromFormat("%S()", classname, pieces);
|
||||||
args = PyTuple_Pack(1, classname);
|
else
|
||||||
}
|
result = PyUnicode_FromFormat("%S(%R)", classname, pieces);
|
||||||
else {
|
|
||||||
formatstr = "%s(%r)";
|
|
||||||
args = PyTuple_Pack(2, classname, pieces);
|
|
||||||
}
|
|
||||||
if (args == NULL)
|
|
||||||
goto Done;
|
|
||||||
|
|
||||||
format = PyUnicode_InternFromString(formatstr);
|
|
||||||
if (format == NULL)
|
|
||||||
goto Done;
|
|
||||||
|
|
||||||
result = PyUnicode_Format(format, args);
|
|
||||||
Done:
|
Done:
|
||||||
Py_XDECREF(pieces);
|
Py_XDECREF(pieces);
|
||||||
Py_XDECREF(cls);
|
Py_XDECREF(cls);
|
||||||
Py_XDECREF(classname);
|
Py_XDECREF(classname);
|
||||||
Py_XDECREF(format);
|
|
||||||
Py_XDECREF(args);
|
|
||||||
Py_ReprLeave((PyObject *)self);
|
Py_ReprLeave((PyObject *)self);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
@ -2395,25 +2310,30 @@ static PyObject *
|
||||||
mutablemapping_update(PyObject *self, PyObject *args, PyObject *kwargs)
|
mutablemapping_update(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
{
|
{
|
||||||
int res = 0;
|
int res = 0;
|
||||||
Py_ssize_t len = (args != NULL) ? PyObject_Size(args) : 0;
|
Py_ssize_t len;
|
||||||
|
_Py_IDENTIFIER(items);
|
||||||
|
_Py_IDENTIFIER(keys);
|
||||||
|
|
||||||
/* first handle args, if any */
|
/* first handle args, if any */
|
||||||
if (len < 0) /* PyObject_Size raised an exception. */
|
assert(args == NULL || PyTuple_Check(args));
|
||||||
return NULL;
|
len = (args != NULL) ? PyTuple_GET_SIZE(args) : 0;
|
||||||
|
|
||||||
if (len > 1) {
|
if (len > 1) {
|
||||||
char *msg = "update() takes at most 1 positional argument (%d given)";
|
char *msg = "update() takes at most 1 positional argument (%d given)";
|
||||||
PyErr_Format(PyExc_TypeError, msg, len);
|
PyErr_Format(PyExc_TypeError, msg, len);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len == 1) {
|
if (len) {
|
||||||
PyObject *other = PyTuple_GET_ITEM(args, 0); /* borrowed reference */
|
PyObject *other = PyTuple_GET_ITEM(args, 0); /* borrowed reference */
|
||||||
if (other == NULL)
|
assert(other != NULL);
|
||||||
return NULL;
|
|
||||||
Py_INCREF(other);
|
Py_INCREF(other);
|
||||||
if (PyObject_HasAttrString(other, "items")) { /* never fails */
|
if (PyDict_CheckExact(other) ||
|
||||||
PyObject *items = PyMapping_Items(other);
|
_PyObject_HasAttrId(other, &PyId_items)) { /* never fails */
|
||||||
|
PyObject *items;
|
||||||
|
if (PyDict_CheckExact(other))
|
||||||
|
items = PyDict_Items(other);
|
||||||
|
else
|
||||||
|
items = _PyObject_CallMethodId(other, &PyId_items, NULL);
|
||||||
Py_DECREF(other);
|
Py_DECREF(other);
|
||||||
if (items == NULL)
|
if (items == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -2422,9 +2342,9 @@ mutablemapping_update(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
if (res == -1)
|
if (res == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
else if (PyObject_HasAttrString(other, "keys")) { /* never fails */
|
else if (_PyObject_HasAttrId(other, &PyId_keys)) { /* never fails */
|
||||||
PyObject *keys, *iterator, *key;
|
PyObject *keys, *iterator, *key;
|
||||||
keys = PyObject_CallMethod(other, "keys", NULL);
|
keys = _PyObject_CallMethodIdObjArgs(other, &PyId_keys, NULL);
|
||||||
if (keys == NULL) {
|
if (keys == NULL) {
|
||||||
Py_DECREF(other);
|
Py_DECREF(other);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -2460,16 +2380,10 @@ mutablemapping_update(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now handle kwargs */
|
/* now handle kwargs */
|
||||||
len = (kwargs != NULL) ? PyObject_Size(kwargs) : 0;
|
assert(kwargs == NULL || PyDict_Check(kwargs));
|
||||||
if (len < 0) /* PyObject_Size raised an exception. */
|
len = (kwargs != NULL) ? PyDict_Size(kwargs) : 0;
|
||||||
return NULL;
|
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
PyObject *items;
|
PyObject *items = PyDict_Items(kwargs);
|
||||||
if (!PyMapping_Check(kwargs)) {
|
|
||||||
PyErr_SetString(PyExc_TypeError, "expected mapping for kwargs");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
items = PyMapping_Items(kwargs);
|
|
||||||
if (items == NULL)
|
if (items == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
res = mutablemapping_add_pairs(self, items);
|
res = mutablemapping_add_pairs(self, items);
|
||||||
|
|
Loading…
Reference in New Issue