diff -r 703d9066c459 Include/odictobject.h --- a/Include/odictobject.h Mon Jun 20 21:42:05 2016 +0300 +++ b/Include/odictobject.h Mon Jun 20 22:19:01 2016 -0400 @@ -11,6 +11,7 @@ typedef struct _odictobject PyODictObject; +PyAPI_DATA(PyTypeObject) PyODictMeta_Type; PyAPI_DATA(PyTypeObject) PyODict_Type; PyAPI_DATA(PyTypeObject) PyODictIter_Type; PyAPI_DATA(PyTypeObject) PyODictKeys_Type; diff -r 703d9066c459 Lib/collections/__init__.py --- a/Lib/collections/__init__.py Mon Jun 20 21:42:05 2016 +0300 +++ b/Lib/collections/__init__.py Mon Jun 20 22:19:01 2016 -0400 @@ -68,6 +68,37 @@ class _Link(object): __slots__ = 'prev', 'next', 'key', '__weakref__' + +class _OrderedDictMeta(type): + @staticmethod + def __extract_pairs(key): + if not isinstance(key, tuple): + key = key, + + for n, pair in enumerate(key): + if not isinstance(pair, slice): + raise TypeError( + "invalid field specification at index %d, fields" + " must be specified like: 'key: value'" % n, + ) + + if pair.start is None: + raise TypeError('missing field name at index %d' % n) + if pair.step is not None: + raise TypeError( + "unexpected slice step for field %r at index %d" + " (hint: you might have a second ':')" % (name, n), + ) + if pair.stop is None: + raise TypeError( + "missing value for field %r at index %d" % (name, n), + ) + + yield pair.start, pair.stop + + def __getitem__(self, key): + return self(self.__extract_pairs(key)) + class OrderedDict(dict): 'Dictionary that remembers insertion order' # An inherited dict maps keys to values. diff -r 703d9066c459 Lib/test/test_ordered_dict.py --- a/Lib/test/test_ordered_dict.py Mon Jun 20 21:42:05 2016 +0300 +++ b/Lib/test/test_ordered_dict.py Mon Jun 20 22:19:01 2016 -0400 @@ -614,6 +614,102 @@ support.check_free_after_iterating(self, lambda d: iter(d.values()), self.OrderedDict) support.check_free_after_iterating(self, lambda d: iter(d.items()), self.OrderedDict) + def test_literals_good(self): + # single key: value pair (non-tuple) + self.assertEqual( + self.OrderedDict['a': 1], + self.OrderedDict([('a', 1)]), + ) + + # multiple keys and values + self.assertEqual( + self.OrderedDict['a': 1, 'b': 2], + self.OrderedDict([('a', 1), ('b', 2)]), + ) + + # non string keys + self.assertEqual( + self.OrderedDict[1: 'a', 2: 'b'], + self.OrderedDict([(1, 'a'), (2, 'b')]), + ) + + def test_literals_bad(self): + with self.assertRaises(TypeError) as e: + # non slice + self.OrderedDict['a'] + self.assertEqual( + str(e.exception), + "invalid field specification at index 0, fields" + " must be specified like: 'key: value'", + ) + + with self.assertRaises(TypeError) as e: + # no name, no value + self.OrderedDict[:] + self.assertEqual( + str(e.exception), + 'missing field name at index 0', + ) + + with self.assertRaises(TypeError) as e: + # no name with value + self.OrderedDict[:1] + self.assertEqual( + str(e.exception), + 'missing field name at index 0', + ) + + with self.assertRaises(TypeError) as e: + # no name at nonzero index + self.OrderedDict['a': 1, :2] + self.assertEqual( + str(e.exception), + 'missing field name at index 1', + ) + + with self.assertRaises(TypeError) as e: + # with step + self.OrderedDict['a':1:'step'] + self.assertEqual( + str(e.exception), + "unexpected slice step for field 'a' at index 0" + " (hint: you might have a second ':')", + ) + + with self.assertRaises(TypeError) as e: + # with step and no value + self.OrderedDict['a'::'step'] + self.assertEqual( + str(e.exception), + "unexpected slice step for field 'a' at index 0" + " (hint: you might have a second ':')", + ) + + with self.assertRaises(TypeError) as e: + # with step at nonzero index + self.OrderedDict['a':1, 'b':2:'step'] + self.assertEqual( + str(e.exception), + "unexpected slice step for field 'b' at index 1" + " (hint: you might have a second ':')", + ) + + with self.assertRaises(TypeError) as e: + # without value + self.OrderedDict['a':] + self.assertEqual( + str(e.exception), + "missing value for field 'a' at index 0", + ) + + with self.assertRaises(TypeError) as e: + # without value at nonzero index + self.OrderedDict['a':1, 'b':] + self.assertEqual( + str(e.exception), + "missing value for field 'b' at index 1", + ) + class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase): diff -r 703d9066c459 Objects/object.c --- a/Objects/object.c Mon Jun 20 21:42:05 2016 +0300 +++ b/Objects/object.c Mon Jun 20 22:19:01 2016 -0400 @@ -1595,6 +1595,9 @@ if (PyType_Ready(&PyDict_Type) < 0) Py_FatalError("Can't initialize dict type"); + if (PyType_Ready(&PyODictMeta_Type) < 0) + Py_FatalError("Can't initialize _OrderedDictMeta type"); + if (PyType_Ready(&PyODict_Type) < 0) Py_FatalError("Can't initialize OrderedDict type"); diff -r 703d9066c459 Objects/odictobject.c --- a/Objects/odictobject.c Mon Jun 20 21:42:05 2016 +0300 +++ b/Objects/odictobject.c Mon Jun 20 22:19:01 2016 -0400 @@ -1668,10 +1668,123 @@ return (PyObject*)od; } +/* PyODictMeta */ + +static PyObject * +odictmeta_subscript(PyObject *self, PyObject *key) +{ + Py_ssize_t n; + PyObject *pairs = NULL; + PyObject *pair; + PyObject *item; + PySliceObject *slice_item; + PyObject *odict = NULL; + + if (!PyTuple_Check(key)) { + key = PyTuple_Pack(1, key); + } + else { + Py_INCREF(key); + } + + if (!(pairs = PyTuple_New(PyTuple_GET_SIZE(key)))) { + goto error; + } + + for (n = 0; n < PyTuple_GET_SIZE(key); ++n) { + item = PyTuple_GET_ITEM(key, n); + if (!PySlice_Check(item)) { + PyErr_Format(PyExc_TypeError, + "invalid field specification at index %d, fields" + " must be specified like: 'key: value'", + n); + goto error; + } + + slice_item = (PySliceObject*) item; + + if (slice_item->start == Py_None) { + PyErr_Format(PyExc_TypeError, + "missing field name at index %d", + n); + goto error; + } + if (slice_item->step != Py_None) { + PyErr_Format(PyExc_TypeError, + "unexpected slice step for field %R at index %d" + " (hint: you might have a second ':')", + slice_item->start, + n); + goto error; + } + if (slice_item->stop == Py_None) { + PyErr_Format(PyExc_TypeError, + "missing value for field %R at index %d", + slice_item->start, + n); + goto error; + } + + if (!(pair = PyTuple_Pack(2, slice_item->start, slice_item->stop))) { + goto error; + } + + PyTuple_SET_ITEM(pairs, n, pair); + } + + odict = PyObject_CallFunctionObjArgs(self, pairs, NULL); + +error: + Py_DECREF(key); + Py_XDECREF(pairs); + return odict; +} + +static PyMappingMethods odictmeta_as_mapping = { + 0, /*mp_length*/ + (binaryfunc)odictmeta_subscript, /*mp_subscript*/ + 0, /*mp_ass_subscript*/ +}; + +PyTypeObject PyODictMeta_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "collections._OrderedDictMeta", /* tp_name */ + sizeof(PyTypeObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + &odictmeta_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_TYPE_SUBCLASS, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &PyType_Type, /* tp_base */ +}; + /* PyODict_Type */ PyTypeObject PyODict_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) + PyVarObject_HEAD_INIT(&PyODictMeta_Type, 0) "collections.OrderedDict", /* tp_name */ sizeof(PyODictObject), /* tp_basicsize */ 0, /* tp_itemsize */