mirror of
https://github.com/python/cpython.git
synced 2026-04-13 15:20:52 +00:00
gh-145056: Accept frozendict in xml.etree (#145508)
Element and SubElement of xml.etree.ElementTree now also accept frozendict for attrib. Export _PyDict_CopyAsDict() function.
This commit is contained in:
parent
c0ecf211b2
commit
7bdfce0d3a
5 changed files with 36 additions and 14 deletions
|
|
@ -702,6 +702,9 @@ Functions
|
|||
attributes. *extra* contains additional attributes, given as keyword
|
||||
arguments. Returns an element instance.
|
||||
|
||||
.. versionchanged:: next
|
||||
*attrib* can now be a :class:`frozendict`.
|
||||
|
||||
|
||||
.. function:: tostring(element, encoding="us-ascii", method="xml", *, \
|
||||
xml_declaration=None, default_namespace=None, \
|
||||
|
|
@ -887,6 +890,9 @@ Element Objects
|
|||
an optional dictionary, containing element attributes. *extra* contains
|
||||
additional attributes, given as keyword arguments.
|
||||
|
||||
.. versionchanged:: next
|
||||
*attrib* can now be a :class:`frozendict`.
|
||||
|
||||
|
||||
.. attribute:: tag
|
||||
|
||||
|
|
|
|||
|
|
@ -160,7 +160,8 @@ extern void _PyDict_Clear_LockHeld(PyObject *op);
|
|||
PyAPI_FUNC(void) _PyDict_EnsureSharedOnRead(PyDictObject *mp);
|
||||
#endif
|
||||
|
||||
extern PyObject* _PyDict_CopyAsDict(PyObject *op);
|
||||
// Export for '_elementtree' shared extension
|
||||
PyAPI_FUNC(PyObject*) _PyDict_CopyAsDict(PyObject *op);
|
||||
|
||||
#define DKIX_EMPTY (-1)
|
||||
#define DKIX_DUMMY (-2) /* Used internally */
|
||||
|
|
|
|||
|
|
@ -4472,6 +4472,9 @@ def test_issue14818(self):
|
|||
ET.Element('a', dict(href="#"), id="foo"),
|
||||
ET.Element('a', href="#", id="foo"),
|
||||
ET.Element('a', dict(href="#", id="foo"), href="#", id="foo"),
|
||||
ET.Element('a', frozendict(href="#", id="foo")),
|
||||
ET.Element('a', frozendict(href="#"), id="foo"),
|
||||
ET.Element('a', attrib=frozendict(href="#", id="foo")),
|
||||
]
|
||||
for e in elements:
|
||||
self.assertEqual(e.tag, 'a')
|
||||
|
|
@ -4479,10 +4482,14 @@ def test_issue14818(self):
|
|||
|
||||
e2 = ET.SubElement(elements[0], 'foobar', attrib={'key1': 'value1'})
|
||||
self.assertEqual(e2.attrib['key1'], 'value1')
|
||||
e3 = ET.SubElement(elements[0], 'foobar',
|
||||
attrib=frozendict({'key1': 'value1'}))
|
||||
self.assertEqual(e3.attrib['key1'], 'value1')
|
||||
|
||||
with self.assertRaisesRegex(TypeError, 'must be dict, not str'):
|
||||
errmsg = 'must be dict or frozendict, not str'
|
||||
with self.assertRaisesRegex(TypeError, errmsg):
|
||||
ET.Element('a', "I'm not a dict")
|
||||
with self.assertRaisesRegex(TypeError, 'must be dict, not str'):
|
||||
with self.assertRaisesRegex(TypeError, errmsg):
|
||||
ET.Element('a', attrib="I'm not a dict")
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -165,8 +165,8 @@ class Element:
|
|||
"""
|
||||
|
||||
def __init__(self, tag, attrib={}, **extra):
|
||||
if not isinstance(attrib, dict):
|
||||
raise TypeError("attrib must be dict, not %s" % (
|
||||
if not isinstance(attrib, (dict, frozendict)):
|
||||
raise TypeError("attrib must be dict or frozendict, not %s" % (
|
||||
attrib.__class__.__name__,))
|
||||
self.tag = tag
|
||||
self.attrib = {**attrib, **extra}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#endif
|
||||
|
||||
#include "Python.h"
|
||||
#include "pycore_dict.h" // _PyDict_CopyAsDict()
|
||||
#include "pycore_pyhash.h" // _Py_HashSecret
|
||||
#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
|
||||
|
||||
|
|
@ -382,13 +383,14 @@ get_attrib_from_keywords(PyObject *kwds)
|
|||
/* If attrib was found in kwds, copy its value and remove it from
|
||||
* kwds
|
||||
*/
|
||||
if (!PyDict_Check(attrib)) {
|
||||
PyErr_Format(PyExc_TypeError, "attrib must be dict, not %.100s",
|
||||
Py_TYPE(attrib)->tp_name);
|
||||
if (!PyAnyDict_Check(attrib)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"attrib must be dict or frozendict, not %T",
|
||||
attrib);
|
||||
Py_DECREF(attrib);
|
||||
return NULL;
|
||||
}
|
||||
Py_SETREF(attrib, PyDict_Copy(attrib));
|
||||
Py_SETREF(attrib, _PyDict_CopyAsDict(attrib));
|
||||
}
|
||||
else {
|
||||
attrib = PyDict_New();
|
||||
|
|
@ -416,12 +418,18 @@ element_init(PyObject *self, PyObject *args, PyObject *kwds)
|
|||
PyObject *attrib = NULL;
|
||||
ElementObject *self_elem;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O|O!:Element", &tag, &PyDict_Type, &attrib))
|
||||
if (!PyArg_ParseTuple(args, "O|O:Element", &tag, &attrib))
|
||||
return -1;
|
||||
if (attrib != NULL && !PyAnyDict_Check(attrib)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Element() argument 2 must be dict or frozendict, not %T",
|
||||
attrib);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (attrib) {
|
||||
/* attrib passed as positional arg */
|
||||
attrib = PyDict_Copy(attrib);
|
||||
attrib = _PyDict_CopyAsDict(attrib);
|
||||
if (!attrib)
|
||||
return -1;
|
||||
if (kwds) {
|
||||
|
|
@ -2111,10 +2119,10 @@ static int
|
|||
element_attrib_setter(PyObject *op, PyObject *value, void *closure)
|
||||
{
|
||||
_VALIDATE_ATTR_VALUE(value);
|
||||
if (!PyDict_Check(value)) {
|
||||
if (!PyAnyDict_Check(value)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"attrib must be dict, not %.200s",
|
||||
Py_TYPE(value)->tp_name);
|
||||
"attrib must be dict or frozendict, not %T",
|
||||
value);
|
||||
return -1;
|
||||
}
|
||||
ElementObject *self = _Element_CAST(op);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue