1616import re
1717import sys
1818
19+ try :
20+ from collections .abc import MutableMapping
21+ except ImportError :
22+ from collections import MutableMapping
23+
1924from .import base
2025from ..constants import DataLossWarning
2126from ..import constants
2227from .import etree as etree_builders
2328from ..import _ihatexml
2429
2530import lxml .etree as etree
31+ from six import PY3 ,binary_type
2632
2733
2834fullTree = True
@@ -189,26 +195,37 @@ def __init__(self, namespaceHTMLElements, fullTree=False):
189195infosetFilter = self .infosetFilter = _ihatexml .InfosetFilter (preventDoubleDashComments = True )
190196self .namespaceHTMLElements = namespaceHTMLElements
191197
192- class Attributes (dict ):
193- def __init__ (self ,element ,value = None ):
194- if value is None :
195- value = {}
198+ class Attributes (MutableMapping ):
199+ def __init__ (self ,element ):
196200self ._element = element
197- dict .__init__ (self ,value )# pylint:disable=non-parent-init-called
198- for key ,value in self .items ():
199- if isinstance (key ,tuple ):
200- name = "{%s}%s" % (key [2 ],infosetFilter .coerceAttribute (key [1 ]))
201- else :
202- name = infosetFilter .coerceAttribute (key )
203- self ._element ._element .attrib [name ]= value
204201
205- def __setitem__ (self ,key ,value ):
206- dict .__setitem__ (self ,key ,value )
202+ def _coerceKey (self ,key ):
207203if isinstance (key ,tuple ):
208204name = "{%s}%s" % (key [2 ],infosetFilter .coerceAttribute (key [1 ]))
209205else :
210206name = infosetFilter .coerceAttribute (key )
211- self ._element ._element .attrib [name ]= value
207+ return name
208+
209+ def __getitem__ (self ,key ):
210+ value = self ._element ._element .attrib [self ._coerceKey (key )]
211+ if not PY3 and isinstance (value ,binary_type ):
212+ value = value .decode ("ascii" )
213+ return value
214+
215+ def __setitem__ (self ,key ,value ):
216+ self ._element ._element .attrib [self ._coerceKey (key )]= value
217+
218+ def __delitem__ (self ,key ):
219+ del self ._element ._element .attrib [self ._coerceKey (key )]
220+
221+ def __iter__ (self ):
222+ return iter (self ._element ._element .attrib )
223+
224+ def __len__ (self ):
225+ return len (self ._element ._element .attrib )
226+
227+ def clear (self ):
228+ return self ._element ._element .attrib .clear ()
212229
213230class Element (builder .Element ):
214231def __init__ (self ,name ,namespace ):
@@ -229,17 +246,22 @@ def _getName(self):
229246def _getAttributes (self ):
230247return self ._attributes
231248
232- def _setAttributes (self ,attributes ):
233- self ._attributes = Attributes (self ,attributes )
249+ def _setAttributes (self ,value ):
250+ attributes = self .attributes
251+ attributes .clear ()
252+ attributes .update (value )
234253
235254attributes = property (_getAttributes ,_setAttributes )
236255
237256def insertText (self ,data ,insertBefore = None ):
238257data = infosetFilter .coerceCharacters (data )
239258builder .Element .insertText (self ,data ,insertBefore )
240259
241- def appendChild (self ,child ):
242- builder .Element .appendChild (self ,child )
260+ def cloneNode (self ):
261+ element = type (self )(self .name ,self .namespace )
262+ if self ._element .attrib :
263+ element ._element .attrib .update (self ._element .attrib )
264+ return element
243265
244266class Comment (builder .Comment ):
245267def __init__ (self ,data ):