mirror of
https://github.com/python/cpython.git
synced 2025-07-30 06:34:15 +00:00
#1826: allow dotted attribute paths in operator.attrgetter.
This commit is contained in:
parent
b0b0317ba2
commit
e2065c65d3
4 changed files with 78 additions and 5 deletions
|
@ -499,15 +499,21 @@ expect a function argument.
|
||||||
|
|
||||||
Return a callable object that fetches *attr* from its operand. If more than one
|
Return a callable object that fetches *attr* from its operand. If more than one
|
||||||
attribute is requested, returns a tuple of attributes. After,
|
attribute is requested, returns a tuple of attributes. After,
|
||||||
``f=attrgetter('name')``, the call ``f(b)`` returns ``b.name``. After,
|
``f = attrgetter('name')``, the call ``f(b)`` returns ``b.name``. After,
|
||||||
``f=attrgetter('name', 'date')``, the call ``f(b)`` returns ``(b.name,
|
``f = attrgetter('name', 'date')``, the call ``f(b)`` returns ``(b.name,
|
||||||
b.date)``.
|
b.date)``.
|
||||||
|
|
||||||
|
The attribute names can also contain dots; after ``f = attrgetter('date.month')``,
|
||||||
|
the call ``f(b)`` returns ``b.date.month``.
|
||||||
|
|
||||||
.. versionadded:: 2.4
|
.. versionadded:: 2.4
|
||||||
|
|
||||||
.. versionchanged:: 2.5
|
.. versionchanged:: 2.5
|
||||||
Added support for multiple attributes.
|
Added support for multiple attributes.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.6
|
||||||
|
Added support for dotted attributes.
|
||||||
|
|
||||||
|
|
||||||
.. function:: itemgetter(item[, args...])
|
.. function:: itemgetter(item[, args...])
|
||||||
|
|
||||||
|
|
|
@ -386,6 +386,26 @@ class OperatorTestCase(unittest.TestCase):
|
||||||
raise SyntaxError
|
raise SyntaxError
|
||||||
self.failUnlessRaises(SyntaxError, operator.attrgetter('foo'), C())
|
self.failUnlessRaises(SyntaxError, operator.attrgetter('foo'), C())
|
||||||
|
|
||||||
|
# recursive gets
|
||||||
|
a = A()
|
||||||
|
a.name = 'arthur'
|
||||||
|
a.child = A()
|
||||||
|
a.child.name = 'thomas'
|
||||||
|
f = operator.attrgetter('child.name')
|
||||||
|
self.assertEqual(f(a), 'thomas')
|
||||||
|
self.assertRaises(AttributeError, f, a.child)
|
||||||
|
f = operator.attrgetter('name', 'child.name')
|
||||||
|
self.assertEqual(f(a), ('arthur', 'thomas'))
|
||||||
|
f = operator.attrgetter('name', 'child.name', 'child.child.name')
|
||||||
|
self.assertRaises(AttributeError, f, a)
|
||||||
|
|
||||||
|
a.child.child = A()
|
||||||
|
a.child.child.name = 'johnson'
|
||||||
|
f = operator.attrgetter('child.child.name')
|
||||||
|
self.assertEqual(f(a), 'johnson')
|
||||||
|
f = operator.attrgetter('name', 'child.name', 'child.child.name')
|
||||||
|
self.assertEqual(f(a), ('arthur', 'thomas', 'johnson'))
|
||||||
|
|
||||||
def test_itemgetter(self):
|
def test_itemgetter(self):
|
||||||
a = 'ABCDE'
|
a = 'ABCDE'
|
||||||
f = operator.itemgetter(2)
|
f = operator.itemgetter(2)
|
||||||
|
|
|
@ -1196,6 +1196,8 @@ Library
|
||||||
Extension Modules
|
Extension Modules
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Patch #1826: operator.attrgetter() now supports dotted attribute paths.
|
||||||
|
|
||||||
- Patch #1957: syslogmodule: Release GIL when calling syslog(3)
|
- Patch #1957: syslogmodule: Release GIL when calling syslog(3)
|
||||||
|
|
||||||
- #2112: mmap.error is now a subclass of EnvironmentError and not a
|
- #2112: mmap.error is now a subclass of EnvironmentError and not a
|
||||||
|
|
|
@ -495,6 +495,49 @@ attrgetter_traverse(attrgetterobject *ag, visitproc visit, void *arg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
dotted_getattr(PyObject *obj, PyObject *attr)
|
||||||
|
{
|
||||||
|
char *s, *p;
|
||||||
|
|
||||||
|
#ifdef Py_USING_UNICODE
|
||||||
|
if (PyUnicode_Check(attr)) {
|
||||||
|
attr = _PyUnicode_AsDefaultEncodedString(attr, NULL);
|
||||||
|
if (attr == NULL)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!PyString_Check(attr)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"attribute name must be a string");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = PyString_AS_STRING(attr);
|
||||||
|
Py_INCREF(obj);
|
||||||
|
for (;;) {
|
||||||
|
PyObject *newobj, *str;
|
||||||
|
p = strchr(s, '.');
|
||||||
|
str = p ? PyString_FromStringAndSize(s, (p-s)) :
|
||||||
|
PyString_FromString(s);
|
||||||
|
if (str == NULL) {
|
||||||
|
Py_DECREF(obj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
newobj = PyObject_GetAttr(obj, str);
|
||||||
|
Py_DECREF(str);
|
||||||
|
Py_DECREF(obj);
|
||||||
|
if (newobj == NULL)
|
||||||
|
return NULL;
|
||||||
|
obj = newobj;
|
||||||
|
if (p == NULL) break;
|
||||||
|
s = p+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
attrgetter_call(attrgetterobject *ag, PyObject *args, PyObject *kw)
|
attrgetter_call(attrgetterobject *ag, PyObject *args, PyObject *kw)
|
||||||
{
|
{
|
||||||
|
@ -504,7 +547,7 @@ attrgetter_call(attrgetterobject *ag, PyObject *args, PyObject *kw)
|
||||||
if (!PyArg_UnpackTuple(args, "attrgetter", 1, 1, &obj))
|
if (!PyArg_UnpackTuple(args, "attrgetter", 1, 1, &obj))
|
||||||
return NULL;
|
return NULL;
|
||||||
if (ag->nattrs == 1)
|
if (ag->nattrs == 1)
|
||||||
return PyObject_GetAttr(obj, ag->attr);
|
return dotted_getattr(obj, ag->attr);
|
||||||
|
|
||||||
assert(PyTuple_Check(ag->attr));
|
assert(PyTuple_Check(ag->attr));
|
||||||
assert(PyTuple_GET_SIZE(ag->attr) == nattrs);
|
assert(PyTuple_GET_SIZE(ag->attr) == nattrs);
|
||||||
|
@ -516,7 +559,7 @@ attrgetter_call(attrgetterobject *ag, PyObject *args, PyObject *kw)
|
||||||
for (i=0 ; i < nattrs ; i++) {
|
for (i=0 ; i < nattrs ; i++) {
|
||||||
PyObject *attr, *val;
|
PyObject *attr, *val;
|
||||||
attr = PyTuple_GET_ITEM(ag->attr, i);
|
attr = PyTuple_GET_ITEM(ag->attr, i);
|
||||||
val = PyObject_GetAttr(obj, attr);
|
val = dotted_getattr(obj, attr);
|
||||||
if (val == NULL) {
|
if (val == NULL) {
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -531,7 +574,9 @@ PyDoc_STRVAR(attrgetter_doc,
|
||||||
\n\
|
\n\
|
||||||
Return a callable object that fetches the given attribute(s) from its operand.\n\
|
Return a callable object that fetches the given attribute(s) from its operand.\n\
|
||||||
After, f=attrgetter('name'), the call f(r) returns r.name.\n\
|
After, f=attrgetter('name'), the call f(r) returns r.name.\n\
|
||||||
After, g=attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).");
|
After, g=attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).\n\
|
||||||
|
After, h=attrgetter('name.first', 'name.last'), the call h(r) returns\n\
|
||||||
|
(r.name.first, r.name.last).");
|
||||||
|
|
||||||
static PyTypeObject attrgetter_type = {
|
static PyTypeObject attrgetter_type = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue