mirror of https://github.com/python/cpython
Issue #16991: Add a C implementation of collections.OrderedDict.
This commit is contained in:
parent
0a3297d7d4
commit
96c6af9b20
|
@ -85,6 +85,7 @@
|
||||||
#include "tupleobject.h"
|
#include "tupleobject.h"
|
||||||
#include "listobject.h"
|
#include "listobject.h"
|
||||||
#include "dictobject.h"
|
#include "dictobject.h"
|
||||||
|
#include "odictobject.h"
|
||||||
#include "enumobject.h"
|
#include "enumobject.h"
|
||||||
#include "setobject.h"
|
#include "setobject.h"
|
||||||
#include "methodobject.h"
|
#include "methodobject.h"
|
||||||
|
|
|
@ -27,6 +27,11 @@ typedef struct {
|
||||||
PyObject **ma_values;
|
PyObject **ma_values;
|
||||||
} PyDictObject;
|
} PyDictObject;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
PyDictObject *dv_dict;
|
||||||
|
} _PyDictViewObject;
|
||||||
|
|
||||||
#endif /* Py_LIMITED_API */
|
#endif /* Py_LIMITED_API */
|
||||||
|
|
||||||
PyAPI_DATA(PyTypeObject) PyDict_Type;
|
PyAPI_DATA(PyTypeObject) PyDict_Type;
|
||||||
|
@ -40,9 +45,9 @@ PyAPI_DATA(PyTypeObject) PyDictValues_Type;
|
||||||
#define PyDict_Check(op) \
|
#define PyDict_Check(op) \
|
||||||
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_DICT_SUBCLASS)
|
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_DICT_SUBCLASS)
|
||||||
#define PyDict_CheckExact(op) (Py_TYPE(op) == &PyDict_Type)
|
#define PyDict_CheckExact(op) (Py_TYPE(op) == &PyDict_Type)
|
||||||
#define PyDictKeys_Check(op) (Py_TYPE(op) == &PyDictKeys_Type)
|
#define PyDictKeys_Check(op) (PyObject_IsInstance(op, (PyObject *)&PyDictKeys_Type))
|
||||||
#define PyDictItems_Check(op) (Py_TYPE(op) == &PyDictItems_Type)
|
#define PyDictItems_Check(op) (PyObject_IsInstance(op, (PyObject *)&PyDictItems_Type))
|
||||||
#define PyDictValues_Check(op) (Py_TYPE(op) == &PyDictValues_Type)
|
#define PyDictValues_Check(op) (PyObject_IsInstance(op, (PyObject *)&PyDictValues_Type))
|
||||||
/* This excludes Values, since they are not sets. */
|
/* This excludes Values, since they are not sets. */
|
||||||
# define PyDictViewSet_Check(op) \
|
# define PyDictViewSet_Check(op) \
|
||||||
(PyDictKeys_Check(op) || PyDictItems_Check(op))
|
(PyDictKeys_Check(op) || PyDictItems_Check(op))
|
||||||
|
@ -75,6 +80,7 @@ PyDictKeysObject *_PyDict_NewKeysForClass(void);
|
||||||
PyAPI_FUNC(PyObject *) PyObject_GenericGetDict(PyObject *, void *);
|
PyAPI_FUNC(PyObject *) PyObject_GenericGetDict(PyObject *, void *);
|
||||||
PyAPI_FUNC(int) _PyDict_Next(
|
PyAPI_FUNC(int) _PyDict_Next(
|
||||||
PyObject *mp, Py_ssize_t *pos, PyObject **key, PyObject **value, Py_hash_t *hash);
|
PyObject *mp, Py_ssize_t *pos, PyObject **key, PyObject **value, Py_hash_t *hash);
|
||||||
|
PyObject *_PyDictView_New(PyObject *, PyTypeObject *);
|
||||||
#endif
|
#endif
|
||||||
PyAPI_FUNC(PyObject *) PyDict_Keys(PyObject *mp);
|
PyAPI_FUNC(PyObject *) PyDict_Keys(PyObject *mp);
|
||||||
PyAPI_FUNC(PyObject *) PyDict_Values(PyObject *mp);
|
PyAPI_FUNC(PyObject *) PyDict_Values(PyObject *mp);
|
||||||
|
@ -88,6 +94,9 @@ PyAPI_FUNC(PyObject *) _PyDict_NewPresized(Py_ssize_t minused);
|
||||||
PyAPI_FUNC(void) _PyDict_MaybeUntrack(PyObject *mp);
|
PyAPI_FUNC(void) _PyDict_MaybeUntrack(PyObject *mp);
|
||||||
PyAPI_FUNC(int) _PyDict_HasOnlyStringKeys(PyObject *mp);
|
PyAPI_FUNC(int) _PyDict_HasOnlyStringKeys(PyObject *mp);
|
||||||
Py_ssize_t _PyDict_KeysSize(PyDictKeysObject *keys);
|
Py_ssize_t _PyDict_KeysSize(PyDictKeysObject *keys);
|
||||||
|
PyObject *_PyDict_SizeOf(PyDictObject *);
|
||||||
|
PyObject *_PyDict_Pop(PyDictObject *, PyObject *, PyObject *);
|
||||||
|
PyObject *_PyDict_FromKeys(PyObject *, PyObject *, PyObject *);
|
||||||
#define _PyDict_HasSplitTable(d) ((d)->ma_values != NULL)
|
#define _PyDict_HasSplitTable(d) ((d)->ma_values != NULL)
|
||||||
|
|
||||||
PyAPI_FUNC(int) PyDict_ClearFreeList(void);
|
PyAPI_FUNC(int) PyDict_ClearFreeList(void);
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef Py_ODICTOBJECT_H
|
||||||
|
#define Py_ODICTOBJECT_H
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* OrderedDict */
|
||||||
|
|
||||||
|
#ifndef Py_LIMITED_API
|
||||||
|
|
||||||
|
typedef struct _odictobject PyODictObject;
|
||||||
|
|
||||||
|
PyAPI_DATA(PyTypeObject) PyODict_Type;
|
||||||
|
PyAPI_DATA(PyTypeObject) PyODictIter_Type;
|
||||||
|
PyAPI_DATA(PyTypeObject) PyODictKeys_Type;
|
||||||
|
PyAPI_DATA(PyTypeObject) PyODictItems_Type;
|
||||||
|
PyAPI_DATA(PyTypeObject) PyODictValues_Type;
|
||||||
|
|
||||||
|
#endif /* Py_LIMITED_API */
|
||||||
|
|
||||||
|
#define PyODict_Check(op) PyObject_IsInstance(op, (PyObject *)&PyODict_Type)
|
||||||
|
#define PyODict_CheckExact(op) (Py_TYPE(op) == &PyODict_Type)
|
||||||
|
#define PyODict_SIZE(op) ((PyDictObject *)op)->ma_used
|
||||||
|
#define PyODict_HasKey(od, key) (PyMapping_HasKey(PyObject *)od, key)
|
||||||
|
|
||||||
|
PyAPI_FUNC(PyObject *) PyODict_New(void);
|
||||||
|
PyAPI_FUNC(int) PyODict_SetItem(PyObject *od, PyObject *key, PyObject *item);
|
||||||
|
PyAPI_FUNC(int) PyODict_DelItem(PyObject *od, PyObject *key);
|
||||||
|
|
||||||
|
/* wrappers around PyDict* functions */
|
||||||
|
#define PyODict_GetItem(od, key) PyDict_GetItem((PyObject *)od, key)
|
||||||
|
#define PyODict_Contains(od, key) PyDict_Contains((PyObject *)od, key)
|
||||||
|
#define PyODict_Size(od) PyDict_Size((PyObject *)od)
|
||||||
|
#define PyODict_GetItemString(od, key) \
|
||||||
|
PyDict_GetItemString((PyObject *)od, key)
|
||||||
|
#define Py_ODict_GetItemId(od, key) _PyDict_GetItemId((PyObject *)od, key)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif /* !Py_ODICTOBJECT_H */
|
|
@ -7,7 +7,6 @@ from _collections_abc import *
|
||||||
import _collections_abc
|
import _collections_abc
|
||||||
__all__ += _collections_abc.__all__
|
__all__ += _collections_abc.__all__
|
||||||
|
|
||||||
from _collections import deque, defaultdict
|
|
||||||
from operator import itemgetter as _itemgetter, eq as _eq
|
from operator import itemgetter as _itemgetter, eq as _eq
|
||||||
from keyword import iskeyword as _iskeyword
|
from keyword import iskeyword as _iskeyword
|
||||||
import sys as _sys
|
import sys as _sys
|
||||||
|
@ -16,7 +15,18 @@ from _weakref import proxy as _proxy
|
||||||
from itertools import repeat as _repeat, chain as _chain, starmap as _starmap
|
from itertools import repeat as _repeat, chain as _chain, starmap as _starmap
|
||||||
from reprlib import recursive_repr as _recursive_repr
|
from reprlib import recursive_repr as _recursive_repr
|
||||||
|
|
||||||
MutableSequence.register(deque)
|
try:
|
||||||
|
from _collections import deque
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
MutableSequence.register(deque)
|
||||||
|
|
||||||
|
try:
|
||||||
|
from _collections import defaultdict
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
### OrderedDict
|
### OrderedDict
|
||||||
|
@ -264,6 +274,13 @@ class OrderedDict(dict):
|
||||||
return dict.__eq__(self, other)
|
return dict.__eq__(self, other)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from _collections import OrderedDict
|
||||||
|
except ImportError:
|
||||||
|
# Leave the pure Python version in place.
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
### namedtuple
|
### namedtuple
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
"""Unit tests for collections.py."""
|
"""Unit tests for collections.py."""
|
||||||
|
|
||||||
import unittest, doctest, operator
|
import unittest, doctest, operator
|
||||||
from test.support import TESTFN, forget, unlink
|
from test.support import TESTFN, forget, unlink, import_fresh_module
|
||||||
|
import contextlib
|
||||||
import inspect
|
import inspect
|
||||||
from test import support
|
from test import support
|
||||||
from collections import namedtuple, Counter, OrderedDict, _count_elements
|
from collections import namedtuple, Counter, OrderedDict, _count_elements
|
||||||
|
@ -1609,9 +1610,24 @@ class TestCounter(unittest.TestCase):
|
||||||
### OrderedDict
|
### OrderedDict
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
class TestOrderedDict(unittest.TestCase):
|
py_coll = import_fresh_module('collections', blocked=['_collections'])
|
||||||
|
c_coll = import_fresh_module('collections', fresh=['_collections'])
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def replaced_module(name, replacement):
|
||||||
|
original_module = sys.modules[name]
|
||||||
|
sys.modules[name] = replacement
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
sys.modules[name] = original_module
|
||||||
|
|
||||||
|
|
||||||
|
class OrderedDictTests:
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
OrderedDict([('a', 1), ('b', 2)], None) # too many args
|
OrderedDict([('a', 1), ('b', 2)], None) # too many args
|
||||||
pairs = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]
|
pairs = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]
|
||||||
|
@ -1635,6 +1651,7 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)])
|
[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)])
|
||||||
|
|
||||||
def test_update(self):
|
def test_update(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
OrderedDict().update([('a', 1), ('b', 2)], None) # too many args
|
OrderedDict().update([('a', 1), ('b', 2)], None) # too many args
|
||||||
pairs = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]
|
pairs = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]
|
||||||
|
@ -1675,11 +1692,17 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
self.assertRaises(TypeError, OrderedDict().update, (), ())
|
self.assertRaises(TypeError, OrderedDict().update, (), ())
|
||||||
self.assertRaises(TypeError, OrderedDict.update)
|
self.assertRaises(TypeError, OrderedDict.update)
|
||||||
|
|
||||||
|
self.assertRaises(TypeError, OrderedDict().update, 42)
|
||||||
|
self.assertRaises(TypeError, OrderedDict().update, (), ())
|
||||||
|
self.assertRaises(TypeError, OrderedDict.update)
|
||||||
|
|
||||||
def test_abc(self):
|
def test_abc(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
self.assertIsInstance(OrderedDict(), MutableMapping)
|
self.assertIsInstance(OrderedDict(), MutableMapping)
|
||||||
self.assertTrue(issubclass(OrderedDict, MutableMapping))
|
self.assertTrue(issubclass(OrderedDict, MutableMapping))
|
||||||
|
|
||||||
def test_clear(self):
|
def test_clear(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
||||||
shuffle(pairs)
|
shuffle(pairs)
|
||||||
od = OrderedDict(pairs)
|
od = OrderedDict(pairs)
|
||||||
|
@ -1688,6 +1711,7 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
self.assertEqual(len(od), 0)
|
self.assertEqual(len(od), 0)
|
||||||
|
|
||||||
def test_delitem(self):
|
def test_delitem(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
||||||
od = OrderedDict(pairs)
|
od = OrderedDict(pairs)
|
||||||
del od['a']
|
del od['a']
|
||||||
|
@ -1697,6 +1721,7 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
self.assertEqual(list(od.items()), pairs[:2] + pairs[3:])
|
self.assertEqual(list(od.items()), pairs[:2] + pairs[3:])
|
||||||
|
|
||||||
def test_setitem(self):
|
def test_setitem(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
od = OrderedDict([('d', 1), ('b', 2), ('c', 3), ('a', 4), ('e', 5)])
|
od = OrderedDict([('d', 1), ('b', 2), ('c', 3), ('a', 4), ('e', 5)])
|
||||||
od['c'] = 10 # existing element
|
od['c'] = 10 # existing element
|
||||||
od['f'] = 20 # new element
|
od['f'] = 20 # new element
|
||||||
|
@ -1704,6 +1729,7 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
[('d', 1), ('b', 2), ('c', 10), ('a', 4), ('e', 5), ('f', 20)])
|
[('d', 1), ('b', 2), ('c', 10), ('a', 4), ('e', 5), ('f', 20)])
|
||||||
|
|
||||||
def test_iterators(self):
|
def test_iterators(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
||||||
shuffle(pairs)
|
shuffle(pairs)
|
||||||
od = OrderedDict(pairs)
|
od = OrderedDict(pairs)
|
||||||
|
@ -1720,6 +1746,11 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
self.assertEqual(list(reversed(od.items())), list(reversed(pairs)))
|
self.assertEqual(list(reversed(od.items())), list(reversed(pairs)))
|
||||||
|
|
||||||
def test_detect_deletion_during_iteration(self):
|
def test_detect_deletion_during_iteration(self):
|
||||||
|
# XXX This test should also work under cOrderedDict.
|
||||||
|
if self.module is c_coll:
|
||||||
|
raise unittest.SkipTest("only valid for pure Python OrderedDict")
|
||||||
|
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
od = OrderedDict.fromkeys('abc')
|
od = OrderedDict.fromkeys('abc')
|
||||||
it = iter(od)
|
it = iter(od)
|
||||||
key = next(it)
|
key = next(it)
|
||||||
|
@ -1729,7 +1760,21 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
# The only guarantee that the next() will not succeed
|
# The only guarantee that the next() will not succeed
|
||||||
next(it)
|
next(it)
|
||||||
|
|
||||||
|
def test_sorted_iterators(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
OrderedDict([('a', 1), ('b', 2)], None)
|
||||||
|
pairs = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]
|
||||||
|
od = OrderedDict(pairs)
|
||||||
|
self.assertEqual(sorted(od), [t[0] for t in pairs])
|
||||||
|
self.assertEqual(sorted(od.keys()), [t[0] for t in pairs])
|
||||||
|
self.assertEqual(sorted(od.values()), [t[1] for t in pairs])
|
||||||
|
self.assertEqual(sorted(od.items()), pairs)
|
||||||
|
self.assertEqual(sorted(reversed(od)),
|
||||||
|
sorted([t[0] for t in reversed(pairs)]))
|
||||||
|
|
||||||
def test_popitem(self):
|
def test_popitem(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
||||||
shuffle(pairs)
|
shuffle(pairs)
|
||||||
od = OrderedDict(pairs)
|
od = OrderedDict(pairs)
|
||||||
|
@ -1739,7 +1784,18 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
od.popitem()
|
od.popitem()
|
||||||
self.assertEqual(len(od), 0)
|
self.assertEqual(len(od), 0)
|
||||||
|
|
||||||
|
def test_popitem_last(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
|
pairs = [(i, i) for i in range(30)]
|
||||||
|
|
||||||
|
obj = OrderedDict(pairs)
|
||||||
|
for i in range(8):
|
||||||
|
obj.popitem(True)
|
||||||
|
obj.popitem(True)
|
||||||
|
self.assertEqual(len(obj), 21)
|
||||||
|
|
||||||
def test_pop(self):
|
def test_pop(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
||||||
shuffle(pairs)
|
shuffle(pairs)
|
||||||
od = OrderedDict(pairs)
|
od = OrderedDict(pairs)
|
||||||
|
@ -1764,6 +1820,7 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
m.pop('a')
|
m.pop('a')
|
||||||
|
|
||||||
def test_equality(self):
|
def test_equality(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
||||||
shuffle(pairs)
|
shuffle(pairs)
|
||||||
od1 = OrderedDict(pairs)
|
od1 = OrderedDict(pairs)
|
||||||
|
@ -1779,6 +1836,7 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
self.assertNotEqual(od1, OrderedDict(pairs[:-1]))
|
self.assertNotEqual(od1, OrderedDict(pairs[:-1]))
|
||||||
|
|
||||||
def test_copying(self):
|
def test_copying(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
# Check that ordered dicts are copyable, deepcopyable, picklable,
|
# Check that ordered dicts are copyable, deepcopyable, picklable,
|
||||||
# and have a repr/eval round-trip
|
# and have a repr/eval round-trip
|
||||||
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
||||||
|
@ -1787,9 +1845,14 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
msg = "\ncopy: %s\nod: %s" % (dup, od)
|
msg = "\ncopy: %s\nod: %s" % (dup, od)
|
||||||
self.assertIsNot(dup, od, msg)
|
self.assertIsNot(dup, od, msg)
|
||||||
self.assertEqual(dup, od)
|
self.assertEqual(dup, od)
|
||||||
|
self.assertEqual(list(dup.items()), list(od.items()))
|
||||||
|
self.assertEqual(len(dup), len(od))
|
||||||
|
self.assertEqual(type(dup), type(od))
|
||||||
check(od.copy())
|
check(od.copy())
|
||||||
check(copy.copy(od))
|
check(copy.copy(od))
|
||||||
check(copy.deepcopy(od))
|
check(copy.deepcopy(od))
|
||||||
|
# pickle directly pulls the module, so we have to fake it
|
||||||
|
with replaced_module('collections', self.module):
|
||||||
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
|
||||||
with self.subTest(proto=proto):
|
with self.subTest(proto=proto):
|
||||||
check(pickle.loads(pickle.dumps(od, proto)))
|
check(pickle.loads(pickle.dumps(od, proto)))
|
||||||
|
@ -1800,6 +1863,7 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
check(OrderedDict(od))
|
check(OrderedDict(od))
|
||||||
|
|
||||||
def test_yaml_linkage(self):
|
def test_yaml_linkage(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
# Verify that __reduce__ is setup in a way that supports PyYAML's dump() feature.
|
# Verify that __reduce__ is setup in a way that supports PyYAML's dump() feature.
|
||||||
# In yaml, lists are native but tuples are not.
|
# In yaml, lists are native but tuples are not.
|
||||||
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
||||||
|
@ -1809,6 +1873,7 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
self.assertTrue(all(type(pair)==list for pair in od.__reduce__()[1]))
|
self.assertTrue(all(type(pair)==list for pair in od.__reduce__()[1]))
|
||||||
|
|
||||||
def test_reduce_not_too_fat(self):
|
def test_reduce_not_too_fat(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
# do not save instance dictionary if not needed
|
# do not save instance dictionary if not needed
|
||||||
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
||||||
od = OrderedDict(pairs)
|
od = OrderedDict(pairs)
|
||||||
|
@ -1817,8 +1882,12 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
self.assertIsNotNone(od.__reduce__()[2])
|
self.assertIsNotNone(od.__reduce__()[2])
|
||||||
|
|
||||||
def test_pickle_recursive(self):
|
def test_pickle_recursive(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
od = OrderedDict()
|
od = OrderedDict()
|
||||||
od[1] = od
|
od[1] = od
|
||||||
|
|
||||||
|
# pickle directly pulls the module, so we have to fake it
|
||||||
|
with replaced_module('collections', self.module):
|
||||||
for proto in range(-1, pickle.HIGHEST_PROTOCOL + 1):
|
for proto in range(-1, pickle.HIGHEST_PROTOCOL + 1):
|
||||||
dup = pickle.loads(pickle.dumps(od, proto))
|
dup = pickle.loads(pickle.dumps(od, proto))
|
||||||
self.assertIsNot(dup, od)
|
self.assertIsNot(dup, od)
|
||||||
|
@ -1826,6 +1895,7 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
self.assertIs(dup[1], dup)
|
self.assertIs(dup[1], dup)
|
||||||
|
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
od = OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])
|
od = OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])
|
||||||
self.assertEqual(repr(od),
|
self.assertEqual(repr(od),
|
||||||
"OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])")
|
"OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])")
|
||||||
|
@ -1833,6 +1903,7 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
self.assertEqual(repr(OrderedDict()), "OrderedDict()")
|
self.assertEqual(repr(OrderedDict()), "OrderedDict()")
|
||||||
|
|
||||||
def test_repr_recursive(self):
|
def test_repr_recursive(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
# See issue #9826
|
# See issue #9826
|
||||||
od = OrderedDict.fromkeys('abc')
|
od = OrderedDict.fromkeys('abc')
|
||||||
od['x'] = od
|
od['x'] = od
|
||||||
|
@ -1840,6 +1911,7 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
"OrderedDict([('a', None), ('b', None), ('c', None), ('x', ...)])")
|
"OrderedDict([('a', None), ('b', None), ('c', None), ('x', ...)])")
|
||||||
|
|
||||||
def test_setdefault(self):
|
def test_setdefault(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
|
||||||
shuffle(pairs)
|
shuffle(pairs)
|
||||||
od = OrderedDict(pairs)
|
od = OrderedDict(pairs)
|
||||||
|
@ -1858,16 +1930,19 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
self.assertEqual(Missing().setdefault(5, 9), 9)
|
self.assertEqual(Missing().setdefault(5, 9), 9)
|
||||||
|
|
||||||
def test_reinsert(self):
|
def test_reinsert(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
# Given insert a, insert b, delete a, re-insert a,
|
# Given insert a, insert b, delete a, re-insert a,
|
||||||
# verify that a is now later than b.
|
# verify that a is now later than b.
|
||||||
od = OrderedDict()
|
od = OrderedDict()
|
||||||
od['a'] = 1
|
od['a'] = 1
|
||||||
od['b'] = 2
|
od['b'] = 2
|
||||||
del od['a']
|
del od['a']
|
||||||
|
self.assertEqual(list(od.items()), [('b', 2)])
|
||||||
od['a'] = 1
|
od['a'] = 1
|
||||||
self.assertEqual(list(od.items()), [('b', 2), ('a', 1)])
|
self.assertEqual(list(od.items()), [('b', 2), ('a', 1)])
|
||||||
|
|
||||||
def test_move_to_end(self):
|
def test_move_to_end(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
od = OrderedDict.fromkeys('abcde')
|
od = OrderedDict.fromkeys('abcde')
|
||||||
self.assertEqual(list(od), list('abcde'))
|
self.assertEqual(list(od), list('abcde'))
|
||||||
od.move_to_end('c')
|
od.move_to_end('c')
|
||||||
|
@ -1880,14 +1955,19 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
self.assertEqual(list(od), list('cabde'))
|
self.assertEqual(list(od), list('cabde'))
|
||||||
with self.assertRaises(KeyError):
|
with self.assertRaises(KeyError):
|
||||||
od.move_to_end('x')
|
od.move_to_end('x')
|
||||||
|
with self.assertRaises(KeyError):
|
||||||
|
od.move_to_end('x', 0)
|
||||||
|
|
||||||
def test_sizeof(self):
|
def test_sizeof(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
# Wimpy test: Just verify the reported size is larger than a regular dict
|
# Wimpy test: Just verify the reported size is larger than a regular dict
|
||||||
d = dict(a=1)
|
d = dict(a=1)
|
||||||
od = OrderedDict(**d)
|
od = OrderedDict(**d)
|
||||||
self.assertGreater(sys.getsizeof(od), sys.getsizeof(d))
|
self.assertGreater(sys.getsizeof(od), sys.getsizeof(d))
|
||||||
|
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
def test_override_update(self):
|
def test_override_update(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
# Verify that subclasses can override update() without breaking __init__()
|
# Verify that subclasses can override update() without breaking __init__()
|
||||||
class MyOD(OrderedDict):
|
class MyOD(OrderedDict):
|
||||||
def update(self, *args, **kwds):
|
def update(self, *args, **kwds):
|
||||||
|
@ -1895,18 +1975,101 @@ class TestOrderedDict(unittest.TestCase):
|
||||||
items = [('a', 1), ('c', 3), ('b', 2)]
|
items = [('a', 1), ('c', 3), ('b', 2)]
|
||||||
self.assertEqual(list(MyOD(items).items()), items)
|
self.assertEqual(list(MyOD(items).items()), items)
|
||||||
|
|
||||||
class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
|
|
||||||
type2test = OrderedDict
|
class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
|
||||||
|
|
||||||
|
module = py_coll
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(c_coll, 'requires the C version of the collections module')
|
||||||
|
class CPythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
|
||||||
|
|
||||||
|
module = c_coll
|
||||||
|
|
||||||
|
def test_delitem_hash_collision(self):
|
||||||
|
OrderedDict = self.module.OrderedDict
|
||||||
|
|
||||||
|
class Key:
|
||||||
|
def __init__(self, hash):
|
||||||
|
self._hash = hash
|
||||||
|
self.value = str(id(self))
|
||||||
|
def __hash__(self):
|
||||||
|
return self._hash
|
||||||
|
def __eq__(self, other):
|
||||||
|
try:
|
||||||
|
return self.value == other.value
|
||||||
|
except AttributeError:
|
||||||
|
return False
|
||||||
|
def __repr__(self):
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
def blocking_hash(hash):
|
||||||
|
# See the collision-handling in lookdict (in Objects/dictobject.c).
|
||||||
|
MINSIZE = 8
|
||||||
|
i = (hash & MINSIZE-1)
|
||||||
|
return (i << 2) + i + hash + 1
|
||||||
|
|
||||||
|
COLLIDING = 1
|
||||||
|
|
||||||
|
key = Key(COLLIDING)
|
||||||
|
colliding = Key(COLLIDING)
|
||||||
|
blocking = Key(blocking_hash(COLLIDING))
|
||||||
|
|
||||||
|
od = OrderedDict()
|
||||||
|
od[key] = ...
|
||||||
|
od[blocking] = ...
|
||||||
|
od[colliding] = ...
|
||||||
|
od['after'] = ...
|
||||||
|
|
||||||
|
del od[blocking]
|
||||||
|
del od[colliding]
|
||||||
|
self.assertEqual(list(od.items()), [(key, ...), ('after', ...)])
|
||||||
|
|
||||||
|
|
||||||
|
class PurePythonGeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.type2test = py_coll.OrderedDict
|
||||||
|
|
||||||
def test_popitem(self):
|
def test_popitem(self):
|
||||||
d = self._empty_mapping()
|
d = self._empty_mapping()
|
||||||
self.assertRaises(KeyError, d.popitem)
|
self.assertRaises(KeyError, d.popitem)
|
||||||
|
|
||||||
class MyOrderedDict(OrderedDict):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class SubclassMappingTests(mapping_tests.BasicTestMappingProtocol):
|
@unittest.skipUnless(c_coll, 'requires the C version of the collections module')
|
||||||
type2test = MyOrderedDict
|
class CPythonGeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.type2test = c_coll.OrderedDict
|
||||||
|
|
||||||
|
def test_popitem(self):
|
||||||
|
d = self._empty_mapping()
|
||||||
|
self.assertRaises(KeyError, d.popitem)
|
||||||
|
|
||||||
|
|
||||||
|
class PurePythonSubclassMappingTests(mapping_tests.BasicTestMappingProtocol):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
class MyOrderedDict(py_coll.OrderedDict):
|
||||||
|
pass
|
||||||
|
cls.type2test = MyOrderedDict
|
||||||
|
|
||||||
|
def test_popitem(self):
|
||||||
|
d = self._empty_mapping()
|
||||||
|
self.assertRaises(KeyError, d.popitem)
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(c_coll, 'requires the C version of the collections module')
|
||||||
|
class CPythonSubclassMappingTests(mapping_tests.BasicTestMappingProtocol):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
class MyOrderedDict(c_coll.OrderedDict):
|
||||||
|
pass
|
||||||
|
cls.type2test = MyOrderedDict
|
||||||
|
|
||||||
def test_popitem(self):
|
def test_popitem(self):
|
||||||
d = self._empty_mapping()
|
d = self._empty_mapping()
|
||||||
|
@ -1923,8 +2086,11 @@ def test_main(verbose=None):
|
||||||
NamedTupleDocs = doctest.DocTestSuite(module=collections)
|
NamedTupleDocs = doctest.DocTestSuite(module=collections)
|
||||||
test_classes = [TestNamedTuple, NamedTupleDocs, TestOneTrickPonyABCs,
|
test_classes = [TestNamedTuple, NamedTupleDocs, TestOneTrickPonyABCs,
|
||||||
TestCollectionABCs, TestCounter, TestChainMap,
|
TestCollectionABCs, TestCounter, TestChainMap,
|
||||||
TestOrderedDict, GeneralMappingTests, SubclassMappingTests,
|
PurePythonOrderedDictTests, CPythonOrderedDictTests,
|
||||||
TestUserObjects]
|
PurePythonGeneralMappingTests, CPythonGeneralMappingTests,
|
||||||
|
PurePythonSubclassMappingTests, CPythonSubclassMappingTests,
|
||||||
|
TestUserObjects,
|
||||||
|
]
|
||||||
support.run_unittest(*test_classes)
|
support.run_unittest(*test_classes)
|
||||||
support.run_doctest(collections, verbose)
|
support.run_doctest(collections, verbose)
|
||||||
|
|
||||||
|
|
|
@ -437,6 +437,7 @@ OBJECT_OBJS= \
|
||||||
Objects/listobject.o \
|
Objects/listobject.o \
|
||||||
Objects/longobject.o \
|
Objects/longobject.o \
|
||||||
Objects/dictobject.o \
|
Objects/dictobject.o \
|
||||||
|
Objects/odictobject.o \
|
||||||
Objects/memoryobject.o \
|
Objects/memoryobject.o \
|
||||||
Objects/methodobject.o \
|
Objects/methodobject.o \
|
||||||
Objects/moduleobject.o \
|
Objects/moduleobject.o \
|
||||||
|
|
|
@ -23,6 +23,8 @@ Library
|
||||||
- Issue #24326: Fixed audioop.ratecv() with non-default weightB argument.
|
- Issue #24326: Fixed audioop.ratecv() with non-default weightB argument.
|
||||||
Original patch by David Moore.
|
Original patch by David Moore.
|
||||||
|
|
||||||
|
- Issue #16991: Add a C implementation of OrderedDict.
|
||||||
|
|
||||||
|
|
||||||
What's New in Python 3.5.0 beta 1?
|
What's New in Python 3.5.0 beta 1?
|
||||||
==================================
|
==================================
|
||||||
|
|
|
@ -2263,6 +2263,9 @@ PyInit__collections(void)
|
||||||
Py_INCREF(&defdict_type);
|
Py_INCREF(&defdict_type);
|
||||||
PyModule_AddObject(m, "defaultdict", (PyObject *)&defdict_type);
|
PyModule_AddObject(m, "defaultdict", (PyObject *)&defdict_type);
|
||||||
|
|
||||||
|
Py_INCREF(&PyODict_Type);
|
||||||
|
PyModule_AddObject(m, "OrderedDict", (PyObject *)&PyODict_Type);
|
||||||
|
|
||||||
if (PyType_Ready(&dequeiter_type) < 0)
|
if (PyType_Ready(&dequeiter_type) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
Py_INCREF(&dequeiter_type);
|
Py_INCREF(&dequeiter_type);
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef Py_DICT_COMMON_H
|
||||||
|
#define Py_DICT_COMMON_H
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/* Cached hash code of me_key. */
|
||||||
|
Py_hash_t me_hash;
|
||||||
|
PyObject *me_key;
|
||||||
|
PyObject *me_value; /* This field is only meaningful for combined tables */
|
||||||
|
} PyDictKeyEntry;
|
||||||
|
|
||||||
|
typedef PyDictKeyEntry *(*dict_lookup_func)
|
||||||
|
(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr);
|
||||||
|
|
||||||
|
struct _dictkeysobject {
|
||||||
|
Py_ssize_t dk_refcnt;
|
||||||
|
Py_ssize_t dk_size;
|
||||||
|
dict_lookup_func dk_lookup;
|
||||||
|
Py_ssize_t dk_usable;
|
||||||
|
PyDictKeyEntry dk_entries[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -67,6 +67,7 @@ to the combined-table form.
|
||||||
#define PyDict_MINSIZE_COMBINED 8
|
#define PyDict_MINSIZE_COMBINED 8
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
#include "dict-common.h"
|
||||||
#include "stringlib/eq.h"
|
#include "stringlib/eq.h"
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
|
@ -74,24 +75,6 @@ class dict "PyDictObject *" "&PyDict_Type"
|
||||||
[clinic start generated code]*/
|
[clinic start generated code]*/
|
||||||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/
|
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
/* Cached hash code of me_key. */
|
|
||||||
Py_hash_t me_hash;
|
|
||||||
PyObject *me_key;
|
|
||||||
PyObject *me_value; /* This field is only meaningful for combined tables */
|
|
||||||
} PyDictKeyEntry;
|
|
||||||
|
|
||||||
typedef PyDictKeyEntry *(*dict_lookup_func)
|
|
||||||
(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr);
|
|
||||||
|
|
||||||
struct _dictkeysobject {
|
|
||||||
Py_ssize_t dk_refcnt;
|
|
||||||
Py_ssize_t dk_size;
|
|
||||||
dict_lookup_func dk_lookup;
|
|
||||||
Py_ssize_t dk_usable;
|
|
||||||
PyDictKeyEntry dk_entries[1];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
To ensure the lookup algorithm terminates, there must be at least one Unused
|
To ensure the lookup algorithm terminates, there must be at least one Unused
|
||||||
|
@ -1425,6 +1408,141 @@ _PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Internal version of dict.pop(). */
|
||||||
|
PyObject *
|
||||||
|
_PyDict_Pop(PyDictObject *mp, PyObject *key, PyObject *deflt)
|
||||||
|
{
|
||||||
|
Py_hash_t hash;
|
||||||
|
PyObject *old_value, *old_key;
|
||||||
|
PyDictKeyEntry *ep;
|
||||||
|
PyObject **value_addr;
|
||||||
|
|
||||||
|
if (mp->ma_used == 0) {
|
||||||
|
if (deflt) {
|
||||||
|
Py_INCREF(deflt);
|
||||||
|
return deflt;
|
||||||
|
}
|
||||||
|
_PyErr_SetKeyError(key);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!PyUnicode_CheckExact(key) ||
|
||||||
|
(hash = ((PyASCIIObject *) key)->hash) == -1) {
|
||||||
|
hash = PyObject_Hash(key);
|
||||||
|
if (hash == -1)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ep = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr);
|
||||||
|
if (ep == NULL)
|
||||||
|
return NULL;
|
||||||
|
old_value = *value_addr;
|
||||||
|
if (old_value == NULL) {
|
||||||
|
if (deflt) {
|
||||||
|
Py_INCREF(deflt);
|
||||||
|
return deflt;
|
||||||
|
}
|
||||||
|
_PyErr_SetKeyError(key);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
*value_addr = NULL;
|
||||||
|
mp->ma_used--;
|
||||||
|
if (!_PyDict_HasSplitTable(mp)) {
|
||||||
|
ENSURE_ALLOWS_DELETIONS(mp);
|
||||||
|
old_key = ep->me_key;
|
||||||
|
Py_INCREF(dummy);
|
||||||
|
ep->me_key = dummy;
|
||||||
|
Py_DECREF(old_key);
|
||||||
|
}
|
||||||
|
return old_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Internal version of dict.from_keys(). It is subclass-friendly. */
|
||||||
|
PyObject *
|
||||||
|
_PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
|
||||||
|
{
|
||||||
|
PyObject *it; /* iter(iterable) */
|
||||||
|
PyObject *key;
|
||||||
|
PyObject *d;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
d = PyObject_CallObject(cls, NULL);
|
||||||
|
if (d == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (PyDict_CheckExact(d) && ((PyDictObject *)d)->ma_used == 0) {
|
||||||
|
if (PyDict_CheckExact(iterable)) {
|
||||||
|
PyDictObject *mp = (PyDictObject *)d;
|
||||||
|
PyObject *oldvalue;
|
||||||
|
Py_ssize_t pos = 0;
|
||||||
|
PyObject *key;
|
||||||
|
Py_hash_t hash;
|
||||||
|
|
||||||
|
if (dictresize(mp, Py_SIZE(iterable))) {
|
||||||
|
Py_DECREF(d);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) {
|
||||||
|
if (insertdict(mp, key, hash, value)) {
|
||||||
|
Py_DECREF(d);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
if (PyAnySet_CheckExact(iterable)) {
|
||||||
|
PyDictObject *mp = (PyDictObject *)d;
|
||||||
|
Py_ssize_t pos = 0;
|
||||||
|
PyObject *key;
|
||||||
|
Py_hash_t hash;
|
||||||
|
|
||||||
|
if (dictresize(mp, PySet_GET_SIZE(iterable))) {
|
||||||
|
Py_DECREF(d);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (_PySet_NextEntry(iterable, &pos, &key, &hash)) {
|
||||||
|
if (insertdict(mp, key, hash, value)) {
|
||||||
|
Py_DECREF(d);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it = PyObject_GetIter(iterable);
|
||||||
|
if (it == NULL){
|
||||||
|
Py_DECREF(d);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyDict_CheckExact(d)) {
|
||||||
|
while ((key = PyIter_Next(it)) != NULL) {
|
||||||
|
status = PyDict_SetItem(d, key, value);
|
||||||
|
Py_DECREF(key);
|
||||||
|
if (status < 0)
|
||||||
|
goto Fail;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while ((key = PyIter_Next(it)) != NULL) {
|
||||||
|
status = PyObject_SetItem(d, key, value);
|
||||||
|
Py_DECREF(key);
|
||||||
|
if (status < 0)
|
||||||
|
goto Fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyErr_Occurred())
|
||||||
|
goto Fail;
|
||||||
|
Py_DECREF(it);
|
||||||
|
return d;
|
||||||
|
|
||||||
|
Fail:
|
||||||
|
Py_DECREF(it);
|
||||||
|
Py_DECREF(d);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Methods */
|
/* Methods */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1763,88 +1881,7 @@ static PyObject *
|
||||||
dict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value)
|
dict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value)
|
||||||
/*[clinic end generated code: output=8fb98e4b10384999 input=b85a667f9bf4669d]*/
|
/*[clinic end generated code: output=8fb98e4b10384999 input=b85a667f9bf4669d]*/
|
||||||
{
|
{
|
||||||
PyObject *it; /* iter(seq) */
|
return _PyDict_FromKeys((PyObject *)type, iterable, value);
|
||||||
PyObject *key;
|
|
||||||
PyObject *d;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
d = PyObject_CallObject((PyObject *)type, NULL);
|
|
||||||
if (d == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (PyDict_CheckExact(d) && ((PyDictObject *)d)->ma_used == 0) {
|
|
||||||
if (PyDict_CheckExact(iterable)) {
|
|
||||||
PyDictObject *mp = (PyDictObject *)d;
|
|
||||||
PyObject *oldvalue;
|
|
||||||
Py_ssize_t pos = 0;
|
|
||||||
PyObject *key;
|
|
||||||
Py_hash_t hash;
|
|
||||||
|
|
||||||
if (dictresize(mp, Py_SIZE(iterable))) {
|
|
||||||
Py_DECREF(d);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) {
|
|
||||||
if (insertdict(mp, key, hash, value)) {
|
|
||||||
Py_DECREF(d);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
if (PyAnySet_CheckExact(iterable)) {
|
|
||||||
PyDictObject *mp = (PyDictObject *)d;
|
|
||||||
Py_ssize_t pos = 0;
|
|
||||||
PyObject *key;
|
|
||||||
Py_hash_t hash;
|
|
||||||
|
|
||||||
if (dictresize(mp, PySet_GET_SIZE(iterable))) {
|
|
||||||
Py_DECREF(d);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (_PySet_NextEntry(iterable, &pos, &key, &hash)) {
|
|
||||||
if (insertdict(mp, key, hash, value)) {
|
|
||||||
Py_DECREF(d);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
it = PyObject_GetIter(iterable);
|
|
||||||
if (it == NULL){
|
|
||||||
Py_DECREF(d);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PyDict_CheckExact(d)) {
|
|
||||||
while ((key = PyIter_Next(it)) != NULL) {
|
|
||||||
status = PyDict_SetItem(d, key, value);
|
|
||||||
Py_DECREF(key);
|
|
||||||
if (status < 0)
|
|
||||||
goto Fail;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while ((key = PyIter_Next(it)) != NULL) {
|
|
||||||
status = PyObject_SetItem(d, key, value);
|
|
||||||
Py_DECREF(key);
|
|
||||||
if (status < 0)
|
|
||||||
goto Fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PyErr_Occurred())
|
|
||||||
goto Fail;
|
|
||||||
Py_DECREF(it);
|
|
||||||
return d;
|
|
||||||
|
|
||||||
Fail:
|
|
||||||
Py_DECREF(it);
|
|
||||||
Py_DECREF(d);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -2356,50 +2393,12 @@ dict_clear(PyDictObject *mp)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
dict_pop(PyDictObject *mp, PyObject *args)
|
dict_pop(PyDictObject *mp, PyObject *args)
|
||||||
{
|
{
|
||||||
Py_hash_t hash;
|
|
||||||
PyObject *old_value, *old_key;
|
|
||||||
PyObject *key, *deflt = NULL;
|
PyObject *key, *deflt = NULL;
|
||||||
PyDictKeyEntry *ep;
|
|
||||||
PyObject **value_addr;
|
|
||||||
|
|
||||||
if(!PyArg_UnpackTuple(args, "pop", 1, 2, &key, &deflt))
|
if(!PyArg_UnpackTuple(args, "pop", 1, 2, &key, &deflt))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (mp->ma_used == 0) {
|
|
||||||
if (deflt) {
|
return _PyDict_Pop(mp, key, deflt);
|
||||||
Py_INCREF(deflt);
|
|
||||||
return deflt;
|
|
||||||
}
|
|
||||||
_PyErr_SetKeyError(key);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (!PyUnicode_CheckExact(key) ||
|
|
||||||
(hash = ((PyASCIIObject *) key)->hash) == -1) {
|
|
||||||
hash = PyObject_Hash(key);
|
|
||||||
if (hash == -1)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
ep = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr);
|
|
||||||
if (ep == NULL)
|
|
||||||
return NULL;
|
|
||||||
old_value = *value_addr;
|
|
||||||
if (old_value == NULL) {
|
|
||||||
if (deflt) {
|
|
||||||
Py_INCREF(deflt);
|
|
||||||
return deflt;
|
|
||||||
}
|
|
||||||
_PyErr_SetKeyError(key);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
*value_addr = NULL;
|
|
||||||
mp->ma_used--;
|
|
||||||
if (!_PyDict_HasSplitTable(mp)) {
|
|
||||||
ENSURE_ALLOWS_DELETIONS(mp);
|
|
||||||
old_key = ep->me_key;
|
|
||||||
Py_INCREF(dummy);
|
|
||||||
ep->me_key = dummy;
|
|
||||||
Py_DECREF(old_key);
|
|
||||||
}
|
|
||||||
return old_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -2506,8 +2505,8 @@ dict_tp_clear(PyObject *op)
|
||||||
|
|
||||||
static PyObject *dictiter_new(PyDictObject *, PyTypeObject *);
|
static PyObject *dictiter_new(PyDictObject *, PyTypeObject *);
|
||||||
|
|
||||||
static PyObject *
|
PyObject *
|
||||||
dict_sizeof(PyDictObject *mp)
|
_PyDict_SizeOf(PyDictObject *mp)
|
||||||
{
|
{
|
||||||
Py_ssize_t size, res;
|
Py_ssize_t size, res;
|
||||||
|
|
||||||
|
@ -2575,7 +2574,7 @@ static PyMethodDef mapp_methods[] = {
|
||||||
DICT___CONTAINS___METHODDEF
|
DICT___CONTAINS___METHODDEF
|
||||||
{"__getitem__", (PyCFunction)dict_subscript, METH_O | METH_COEXIST,
|
{"__getitem__", (PyCFunction)dict_subscript, METH_O | METH_COEXIST,
|
||||||
getitem__doc__},
|
getitem__doc__},
|
||||||
{"__sizeof__", (PyCFunction)dict_sizeof, METH_NOARGS,
|
{"__sizeof__", (PyCFunction)_PyDict_SizeOf, METH_NOARGS,
|
||||||
sizeof__doc__},
|
sizeof__doc__},
|
||||||
{"get", (PyCFunction)dict_get, METH_VARARGS,
|
{"get", (PyCFunction)dict_get, METH_VARARGS,
|
||||||
get__doc__},
|
get__doc__},
|
||||||
|
@ -3104,8 +3103,8 @@ static PyObject *dictiter_iternextitem(dictiterobject *di)
|
||||||
value = *value_ptr;
|
value = *value_ptr;
|
||||||
Py_INCREF(key);
|
Py_INCREF(key);
|
||||||
Py_INCREF(value);
|
Py_INCREF(value);
|
||||||
PyTuple_SET_ITEM(result, 0, key);
|
PyTuple_SET_ITEM(result, 0, key); /* steals reference */
|
||||||
PyTuple_SET_ITEM(result, 1, value);
|
PyTuple_SET_ITEM(result, 1, value); /* steals reference */
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
@ -3200,28 +3199,22 @@ dictiter_reduce(dictiterobject *di)
|
||||||
|
|
||||||
/* The instance lay-out is the same for all three; but the type differs. */
|
/* The instance lay-out is the same for all three; but the type differs. */
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PyObject_HEAD
|
|
||||||
PyDictObject *dv_dict;
|
|
||||||
} dictviewobject;
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dictview_dealloc(dictviewobject *dv)
|
dictview_dealloc(_PyDictViewObject *dv)
|
||||||
{
|
{
|
||||||
Py_XDECREF(dv->dv_dict);
|
Py_XDECREF(dv->dv_dict);
|
||||||
PyObject_GC_Del(dv);
|
PyObject_GC_Del(dv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
dictview_traverse(dictviewobject *dv, visitproc visit, void *arg)
|
dictview_traverse(_PyDictViewObject *dv, visitproc visit, void *arg)
|
||||||
{
|
{
|
||||||
Py_VISIT(dv->dv_dict);
|
Py_VISIT(dv->dv_dict);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Py_ssize_t
|
static Py_ssize_t
|
||||||
dictview_len(dictviewobject *dv)
|
dictview_len(_PyDictViewObject *dv)
|
||||||
{
|
{
|
||||||
Py_ssize_t len = 0;
|
Py_ssize_t len = 0;
|
||||||
if (dv->dv_dict != NULL)
|
if (dv->dv_dict != NULL)
|
||||||
|
@ -3229,10 +3222,10 @@ dictview_len(dictviewobject *dv)
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
PyObject *
|
||||||
dictview_new(PyObject *dict, PyTypeObject *type)
|
_PyDictView_New(PyObject *dict, PyTypeObject *type)
|
||||||
{
|
{
|
||||||
dictviewobject *dv;
|
_PyDictViewObject *dv;
|
||||||
if (dict == NULL) {
|
if (dict == NULL) {
|
||||||
PyErr_BadInternalCall();
|
PyErr_BadInternalCall();
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -3244,7 +3237,7 @@ dictview_new(PyObject *dict, PyTypeObject *type)
|
||||||
type->tp_name, dict->ob_type->tp_name);
|
type->tp_name, dict->ob_type->tp_name);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
dv = PyObject_GC_New(dictviewobject, type);
|
dv = PyObject_GC_New(_PyDictViewObject, type);
|
||||||
if (dv == NULL)
|
if (dv == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
Py_INCREF(dict);
|
Py_INCREF(dict);
|
||||||
|
@ -3348,7 +3341,7 @@ dictview_richcompare(PyObject *self, PyObject *other, int op)
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
dictview_repr(dictviewobject *dv)
|
dictview_repr(_PyDictViewObject *dv)
|
||||||
{
|
{
|
||||||
PyObject *seq;
|
PyObject *seq;
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
|
@ -3365,7 +3358,7 @@ dictview_repr(dictviewobject *dv)
|
||||||
/*** dict_keys ***/
|
/*** dict_keys ***/
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
dictkeys_iter(dictviewobject *dv)
|
dictkeys_iter(_PyDictViewObject *dv)
|
||||||
{
|
{
|
||||||
if (dv->dv_dict == NULL) {
|
if (dv->dv_dict == NULL) {
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
@ -3374,7 +3367,7 @@ dictkeys_iter(dictviewobject *dv)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
dictkeys_contains(dictviewobject *dv, PyObject *obj)
|
dictkeys_contains(_PyDictViewObject *dv, PyObject *obj)
|
||||||
{
|
{
|
||||||
if (dv->dv_dict == NULL)
|
if (dv->dv_dict == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -3499,7 +3492,7 @@ dictviews_isdisjoint(PyObject *self, PyObject *other)
|
||||||
PyObject *item = NULL;
|
PyObject *item = NULL;
|
||||||
|
|
||||||
if (self == other) {
|
if (self == other) {
|
||||||
if (dictview_len((dictviewobject *)self) == 0)
|
if (dictview_len((_PyDictViewObject *)self) == 0)
|
||||||
Py_RETURN_TRUE;
|
Py_RETURN_TRUE;
|
||||||
else
|
else
|
||||||
Py_RETURN_FALSE;
|
Py_RETURN_FALSE;
|
||||||
|
@ -3508,7 +3501,7 @@ dictviews_isdisjoint(PyObject *self, PyObject *other)
|
||||||
/* Iterate over the shorter object (only if other is a set,
|
/* Iterate over the shorter object (only if other is a set,
|
||||||
* because PySequence_Contains may be expensive otherwise): */
|
* because PySequence_Contains may be expensive otherwise): */
|
||||||
if (PyAnySet_Check(other) || PyDictViewSet_Check(other)) {
|
if (PyAnySet_Check(other) || PyDictViewSet_Check(other)) {
|
||||||
Py_ssize_t len_self = dictview_len((dictviewobject *)self);
|
Py_ssize_t len_self = dictview_len((_PyDictViewObject *)self);
|
||||||
Py_ssize_t len_other = PyObject_Size(other);
|
Py_ssize_t len_other = PyObject_Size(other);
|
||||||
if (len_other == -1)
|
if (len_other == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -3555,7 +3548,7 @@ static PyMethodDef dictkeys_methods[] = {
|
||||||
PyTypeObject PyDictKeys_Type = {
|
PyTypeObject PyDictKeys_Type = {
|
||||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||||
"dict_keys", /* tp_name */
|
"dict_keys", /* tp_name */
|
||||||
sizeof(dictviewobject), /* tp_basicsize */
|
sizeof(_PyDictViewObject), /* tp_basicsize */
|
||||||
0, /* tp_itemsize */
|
0, /* tp_itemsize */
|
||||||
/* methods */
|
/* methods */
|
||||||
(destructor)dictview_dealloc, /* tp_dealloc */
|
(destructor)dictview_dealloc, /* tp_dealloc */
|
||||||
|
@ -3588,13 +3581,13 @@ PyTypeObject PyDictKeys_Type = {
|
||||||
static PyObject *
|
static PyObject *
|
||||||
dictkeys_new(PyObject *dict)
|
dictkeys_new(PyObject *dict)
|
||||||
{
|
{
|
||||||
return dictview_new(dict, &PyDictKeys_Type);
|
return _PyDictView_New(dict, &PyDictKeys_Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** dict_items ***/
|
/*** dict_items ***/
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
dictitems_iter(dictviewobject *dv)
|
dictitems_iter(_PyDictViewObject *dv)
|
||||||
{
|
{
|
||||||
if (dv->dv_dict == NULL) {
|
if (dv->dv_dict == NULL) {
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
@ -3603,7 +3596,7 @@ dictitems_iter(dictviewobject *dv)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
dictitems_contains(dictviewobject *dv, PyObject *obj)
|
dictitems_contains(_PyDictViewObject *dv, PyObject *obj)
|
||||||
{
|
{
|
||||||
PyObject *key, *value, *found;
|
PyObject *key, *value, *found;
|
||||||
if (dv->dv_dict == NULL)
|
if (dv->dv_dict == NULL)
|
||||||
|
@ -3641,7 +3634,7 @@ static PyMethodDef dictitems_methods[] = {
|
||||||
PyTypeObject PyDictItems_Type = {
|
PyTypeObject PyDictItems_Type = {
|
||||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||||
"dict_items", /* tp_name */
|
"dict_items", /* tp_name */
|
||||||
sizeof(dictviewobject), /* tp_basicsize */
|
sizeof(_PyDictViewObject), /* tp_basicsize */
|
||||||
0, /* tp_itemsize */
|
0, /* tp_itemsize */
|
||||||
/* methods */
|
/* methods */
|
||||||
(destructor)dictview_dealloc, /* tp_dealloc */
|
(destructor)dictview_dealloc, /* tp_dealloc */
|
||||||
|
@ -3674,13 +3667,13 @@ PyTypeObject PyDictItems_Type = {
|
||||||
static PyObject *
|
static PyObject *
|
||||||
dictitems_new(PyObject *dict)
|
dictitems_new(PyObject *dict)
|
||||||
{
|
{
|
||||||
return dictview_new(dict, &PyDictItems_Type);
|
return _PyDictView_New(dict, &PyDictItems_Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** dict_values ***/
|
/*** dict_values ***/
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
dictvalues_iter(dictviewobject *dv)
|
dictvalues_iter(_PyDictViewObject *dv)
|
||||||
{
|
{
|
||||||
if (dv->dv_dict == NULL) {
|
if (dv->dv_dict == NULL) {
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
@ -3706,7 +3699,7 @@ static PyMethodDef dictvalues_methods[] = {
|
||||||
PyTypeObject PyDictValues_Type = {
|
PyTypeObject PyDictValues_Type = {
|
||||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||||
"dict_values", /* tp_name */
|
"dict_values", /* tp_name */
|
||||||
sizeof(dictviewobject), /* tp_basicsize */
|
sizeof(_PyDictViewObject), /* tp_basicsize */
|
||||||
0, /* tp_itemsize */
|
0, /* tp_itemsize */
|
||||||
/* methods */
|
/* methods */
|
||||||
(destructor)dictview_dealloc, /* tp_dealloc */
|
(destructor)dictview_dealloc, /* tp_dealloc */
|
||||||
|
@ -3739,7 +3732,7 @@ PyTypeObject PyDictValues_Type = {
|
||||||
static PyObject *
|
static PyObject *
|
||||||
dictvalues_new(PyObject *dict)
|
dictvalues_new(PyObject *dict)
|
||||||
{
|
{
|
||||||
return dictview_new(dict, &PyDictValues_Type);
|
return _PyDictView_New(dict, &PyDictValues_Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns NULL if cannot allocate a new PyDictKeysObject,
|
/* Returns NULL if cannot allocate a new PyDictKeysObject,
|
||||||
|
|
|
@ -1607,6 +1607,21 @@ _Py_ReadyTypes(void)
|
||||||
if (PyType_Ready(&PyDict_Type) < 0)
|
if (PyType_Ready(&PyDict_Type) < 0)
|
||||||
Py_FatalError("Can't initialize dict type");
|
Py_FatalError("Can't initialize dict type");
|
||||||
|
|
||||||
|
if (PyType_Ready(&PyODict_Type) < 0)
|
||||||
|
Py_FatalError("Can't initialize OrderedDict type");
|
||||||
|
|
||||||
|
if (PyType_Ready(&PyODictKeys_Type) < 0)
|
||||||
|
Py_FatalError("Can't initialize odict_keys type");
|
||||||
|
|
||||||
|
if (PyType_Ready(&PyODictItems_Type) < 0)
|
||||||
|
Py_FatalError("Can't initialize odict_items type");
|
||||||
|
|
||||||
|
if (PyType_Ready(&PyODictValues_Type) < 0)
|
||||||
|
Py_FatalError("Can't initialize odict_values type");
|
||||||
|
|
||||||
|
if (PyType_Ready(&PyODictIter_Type) < 0)
|
||||||
|
Py_FatalError("Can't initialize odict_keyiterator type");
|
||||||
|
|
||||||
if (PyType_Ready(&PySet_Type) < 0)
|
if (PyType_Ready(&PySet_Type) < 0)
|
||||||
Py_FatalError("Can't initialize set type");
|
Py_FatalError("Can't initialize set type");
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue