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, \
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
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 */
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')
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")
# --------------------------------------------------------------------
"""
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}
#endif
#include "Python.h"
+#include "pycore_dict.h" // _PyDict_CopyAsDict()
#include "pycore_pyhash.h" // _Py_HashSecret
#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
/* 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();
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) {
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);