Module lxmlmate
[hide private]
[frames] | no frames]

Source Code for Module lxmlmate

  1  # -*- coding: utf-8 -*- 
  2   
  3  ''' 
  4  lxml mate. 
  5  ''' 
  6   
  7   
  8  __ver_major__ = 0 
  9  __ver_minor__ = 5 
 10  __ver_patch__ = 2 
 11  __ver_sub__ = "" 
 12  __version__ = "%d.%d.%d%s" % (__ver_major__,__ver_minor__,__ver_patch__,__ver_sub__)  
 13   
 14   
 15   
 16  from lxml import etree, objectify 
 17  import types 
18 19 20 21 -class ObjectifiedElementProxy( object ):
22 u'''Proxy class for objectify.ObjectifiedElement instance which can be created by objectify.Element() or SubElement() or XML() or fromstring(). 23 24 main purpose is to intercept AttributeException when access a non-existent tag. 25 26 How to access xml tag 27 --------------------- 28 . 29 such as root.element.name 30 31 [] 32 such as root['element']['name'] 33 34 hybrid 35 such as root['element'].name 36 37 note 38 The tag can only be accessed by [] when it is one of the reserved keywords. 39 Tag not in the xml tree can be accessed directly. A new tag will be created. No exceptions will be raised. 40 41 42 How to access xml tag's attributes 43 ---------------------------------- 44 [] 45 .attrib['style'], an exception will be raised when style dos'nt exist. 46 .attrib['style'] = 'big' 47 .get/set 48 .attrib.get( 'style', None ) 49 .attrib.set( 'style', 'big' ) 50 51 52 How to access class attributes and methods 53 ------------------------------------------ 54 . 55 attributes are reserved keywords and they can only be accessed by this way, for example 56 .pyval 57 .text 58 .insert etc. 59 or they are considered xml tags rather than attributes. 60 61 62 Reserved keywords 63 ----------------- 64 The following keywords are used as methods or attributes' names. 65 66 **pyval** : returns the python value carried by leaf tag. read-only. 67 68 **text** : returns leaf tag's text content. read-only. 69 70 **obj** : returns ObjectifiedElement object referenced by this class instance. read-only. 71 72 **tag** : returns tag names. can be modified by . way such as \*.tag='newTagName'. readable and writable. 73 74 **attrib** : returns tag attributes dict. readable and writeable. 75 76 **parent** : returns parent node. read-only. 77 78 **children** : returns all children's list. read-only. 79 80 **len** : returns the number of children. 81 82 **insert** : insert a child node at the specified position. 83 84 **remove** : remove a child node at the specified position. 85 86 **index** : returns the position of the specified object. 87 88 **swap** : swap two nodes' position. 89 90 Examples 91 -------- 92 93 create a brand new xml: 94 95 >>> p = ObjectifiedElmentProxy( rootag='Person' ) 96 >>> p.name = 'peter' 97 >>> p.age = 13 98 >>> print( p ) 99 <Person> 100 <name>peter</name> 101 <age>13</age> 102 </Person> 103 104 create from xml string: 105 106 >>> p = ObjectifiedElementProxy( xmlStr="<Person><name>peter</name><age>13</age></Person>" ) 107 >>> print( p ) 108 <Person> 109 <name>peter</name> 110 <age>13</age> 111 </Person> 112 113 multiple levels examples: 114 115 >>> r = ObjectifiedElementProxy() 116 >>> r.person.name = 'jack' 117 >>> r.person.age = 10 118 >>> print( r ) 119 <root> 120 <person> 121 <name>jack</name> 122 <age>10</age> 123 </person> 124 </root> 125 126 to insert child like '<person><name>peter</name><age>13</age></person>': 127 128 >>> r.insert( 'person' )('name','peter')('age',13) 129 130 >>> p = r('person').person[-1] 131 >>> p.name = 'david' 132 >>> p.age = 16 133 >>> print( r ) 134 <root> 135 <person> 136 <name>jack</name> 137 <age>10</age> 138 </person> 139 <person> 140 <name>peter</name> 141 <age>13</age> 142 </person> 143 <person> 144 <name>david</name> 145 <age>16</age> 146 </person> 147 </root> 148 149 >>> print( r.station[1].name.pyval ) 150 peter 151 152 153 Notes 154 ----- 155 xml attrib names and tag names are case insensitive. 156 157 Nodes with text attribute are called leaf nodes. Theoretically, leaf nodes should not have children, but not required. 158 159 ''' 160
161 - def __init__( self, objectifiedElement=None, xmlFileName=None, xmlStr=None, rootag='root', attrib=None, nsmap=None, **kwargs ):
162 u''' 163 164 initialize from ObjectifiedElement or xml file or xml string or create a brand new. 165 166 Arguments 167 --------- 168 objectifiedElement : ObjectifiedElement, optional 169 an ObjectifiedElement object. 170 171 xmlFileName : str, optional 172 xml's filename. 173 174 xmlStr : str, optional 175 xml's content. 176 177 rootag : str, optional 178 create ObjectifiedElement instance which root tag's name is rootag. 179 180 attrib, nsmap, kwargs : optional 181 refer to objectify.Element() 182 183 ''' 184 185 self._____o____ = None 186 187 if objectifiedElement is not None: 188 self._____o____ = objectifiedElement 189 190 elif xmlFileName: 191 self._____o____ = objectify.XML( xmlFileName ) 192 193 elif xmlStr: 194 self._____o____ = objectify.fromstring( xmlStr ) 195 196 else: 197 self._____o____ = objectify.Element( rootag, attrib=attrib, nsmap=nsmap, **kwargs )
198 199
200 - def __call__( self, tag, pyval=None, attrib=None, nsmap=None, **kwargs ):
201 u'''Insert a new child node. 202 203 insert a new child node to the end. 204 205 Arguments 206 --------- 207 e : str 208 the new tag to be inserted. 209 pyval : legal python data type 210 tag's python value. 211 attrib,nsmap,kwargs : optional 212 attribs for the new tag. see also objectify.Element() or SubElement(). 213 214 Returns 215 ------- 216 ObjectifiedElementProxy instance 217 218 See Also 219 -------- 220 insert 221 222 note the difference between the two methods' return values. 223 224 Examples 225 -------- 226 >>> p=ObjectifiedElementProxy( rootag='Person' ) 227 >>> p( 'name', pyval='jack' )('age', pyval=13 ) 228 >>> print( p ) 229 <Person> 230 <name py:pytype="str">jack</name> 231 <age py:pytype="int">13</age> 232 </Person> 233 ''' 234 235 self.insert( tag, None, attrib, nsmap, **kwargs ) 236 237 self [ tag ][-1] = pyval 238 239 return self
240 241
242 - def __getattr__( self, name ):
243 244 if name == '_____o____': 245 return object.__getattribute__(name) 246 247 if hasattr( self._____o____, name ): 248 e = getattr( self._____o____, name ) 249 250 if name in ( 'tag','pyval','text', 'attrib' ) or isinstance( e, ( types.FunctionType, types.BuiltinFunctionType ) ): 251 return e 252 253 else: 254 #if has no attr named name, created a new one. 255 e = objectify.SubElement( self._____o____, name ) 256 257 258 return ObjectifiedElementProxy( e )
259 260
261 - def __setattr__( self, name, value ):
262 263 if name == '_____o____': 264 object.__setattr__( self, name, value ) 265 return 266 267 setattr( self._____o____, name, value )
268 269
270 - def __delattr__( self, e ):
271 self._____o____.__delattr__( e )
272 273
274 - def __len__( self ):
275 u'''children's number''' 276 277 return len( self.children )
278 279
280 - def __getitem__( self, name ):
281 if isinstance( name, int ): 282 return ObjectifiedElementProxy( self._____o____[name] ) 283 284 if isinstance( name, slice ): 285 return [ ObjectifiedElementProxy( o ) for o in self._____o____[name] ] 286 287 if isinstance( name, str ): 288 if name == '_____o____': 289 return object.__getattribute__( name ) 290 291 o = self._____o____ 292 try: 293 e = o.__getitem__( name ) 294 except: 295 e = objectify.SubElement( self._____o____, name ) 296 297 return ObjectifiedElementProxy( e ) 298 299 raise Exception
300 301
302 - def __setitem__( self, e, v ):
303 if e == '_____o____': 304 object.__setitem__( self, e, v ) 305 return 306 307 self._____o____[e] = v
308 309
310 - def __delitem__( self, e ):
311 if isinstance( e, ObjectifiedElementProxy ): 312 self._____o____.__delattr__( e.tag ) 313 else: 314 self._____o____.__delattr__( e )
315 316
317 - def insert( self, e, i=None, attrib=None, nsmap=None, **kwargs ):
318 u'''Insert a new child node. 319 320 insert a new child node at the specified position. 321 322 Arguments 323 --------- 324 e : str 325 the new tag to be inserted. 326 i : int, optional 327 if i is integer : position of the new tag. else append to the end. 328 attrib,nsmap,kwargs : optional 329 attribs for the new tag. see also objectify.Element() or SubElement(). 330 331 ''' 332 333 v = objectify.SubElement( self._____o____, e, attrib=attrib, nsmap=nsmap, **kwargs ) 334 s = ObjectifiedElementProxy( v ) 335 336 if i is not None: 337 self._____o____.insert( i, v ) 338 339 return s
340 341
342 - def swap( self, i, j ):
343 u'''swap two child nodes' position. 344 345 Arguments 346 --------- 347 i,j : int 348 position of the child nodes to be swapped. 349 ''' 350 351 self._____o____[i] = self._____o____[j]
352 353
354 - def remove( self, i ):
355 u'''remove the child node. 356 357 Arguments 358 --------- 359 i : int or ObjectifiedElement or ObjectifiedElementProxy or list 360 position of the child node or Element which will be removed. 361 362 ''' 363 364 if isinstance( i, list ): 365 for k in i: 366 self.remove( k ) 367 368 elif isinstance( i, int ): 369 return self.obj.remove( self.children[i].obj ) 370 371 elif isinstance( i, objectify.ObjectifiedElement ): 372 return self.obj.remove( i ) 373 374 elif isinstance( i, ObjectifiedElementProxy ): 375 return self.obj.remove( i.obj )
376 377
378 - def index( self, o ):
379 u'''return the position of o. 380 381 Arguments 382 --------- 383 o : ObjectifiedElementProxy 384 the ObjectifiedElementProxy instance to be positioned. 385 386 Returns 387 ------- 388 int 389 ''' 390 391 return self._____o____.index( o.obj )
392 393
394 - def xpath( self, path ):
395 u'''find elements list in accordance with path. 396 397 Arguments 398 --------- 399 path : str 400 please refer to lxml.objectify.ObjectifiedElement.xpath. 401 402 Returns 403 ------- 404 list 405 a list of ObjectifiedElementProxy instance. 406 407 408 Xpath grammer review 409 -------------------- 410 411 ========== =========== 412 expression description 413 ========== =========== 414 nodename to select all children of the node name 415 / select from root node. 416 // select from current node 417 . select the current code. 418 .. select the parent node of the current node. 419 @ select attrib. 420 [] condition 421 text() tag text 422 * arbitrary node 423 ========== ============ 424 ''' 425 426 return [ ObjectifiedElementProxy(k) for k in self._____o____.xpath( path ) ]
427 428 429 @property
430 - def children( self, **kwargs ):
431 return [ ObjectifiedElementProxy( e ) for e in self._____o____.getchildren( **kwargs ) ]
432 433 434 @property
435 - def parent( self ):
436 return ObjectifiedElementProxy( self._____o____.getparent() )
437 438 439 @property
440 - def root( self):
441 parent = self._____o____.getparent() 442 while parent: 443 parent1 = parent.getparent() 444 if parent1 is None: 445 break 446 parent = parent1 447 448 return ObjectifiedElementProxy( parent )
449 450 451 @property
452 - def obj( self ):
453 return self._____o____
454 455 456 @property
457 - def pyval( self ):
458 if hasattr( self._____o____, 'pyval' ): 459 if isinstance( self._____o____, objectify.StringElement ): 460 return self._____o____.pyval.strip() 461 462 return self._____o____.pyval
463 464
465 - def __nonzero__( self ):
466 return self.is_empty()
467 468
469 - def is_empty( self ):
470 u'''To determine whether a null node. 471 472 no text \ no attribs \ no children. 473 ''' 474 475 o = self._____o____ 476 477 if o.text and o.text.strip(): 478 return False 479 480 n = 0 481 for k in o.attrib: 482 if k[0] != '{': 483 n += 1 484 485 if n > 0: 486 return False 487 488 n = 0 489 for c in self.children: 490 if not c.is_empty(): 491 n += 1 492 493 if n > 0: 494 return False 495 496 return True
497 498
499 - def clean( self ):
500 u'''clean all null nodes. 501 ''' 502 503 for c in self.children: 504 if c.is_empty(): 505 c.parent.obj.__delattr__( c.tag ) 506 else: 507 c.clean()
508 509
510 - def tostring( self, encoding='utf-8', xml_declaration=True, standalone=None, with_comments=True, 511 pytype=False, xsi=True, xsi_nil=True, cleanup_namespaces=True, doctype=None, 512 with_tail=True, exclusive=False, inclusive_ns_prefixes=None ):
513 514 #self.clean() 515 516 objectify.deannotate( self._____o____, pytype=pytype, xsi=xsi, xsi_nil=xsi_nil, cleanup_namespaces=cleanup_namespaces ) 517 518 s = etree.tostring( self._____o____, encoding=encoding, pretty_print= True, 519 xml_declaration=xml_declaration, with_tail=with_tail, 520 standalone=standalone, doctype=doctype, 521 exclusive=exclusive, with_comments=with_comments, 522 inclusive_ns_prefixes=inclusive_ns_prefixes ) 523 524 return s
525 526
527 - def __str__( self ):
528 #s = self.tostring( pytype=True, xml_declaration=False , encoding='unicode' ) 529 s = self.tostring( pytype=True, xml_declaration=False ).decode() 530 return s
531 532
533 - def dump( self, xmlFile, encoding='utf-8' ):
534 '''save xml to file. 535 536 Arguments 537 --------- 538 xmlFile : str 539 xml's filename. 540 541 ''' 542 543 f = open( xmlFile, 'w' ) 544 s = self.tostring( encoding=encoding ).decode() 545 f.write( s ) 546 f.close()
547 548 549 550 if __name__ == '__main__': 551 r = ObjectifiedElementProxy() 552 r.person.name = 'jack' 553 r.person.age = 10 554 r.insert( 'person' )('name','peter')('age',13) 555 p = r('person').person[-1] 556 p.name = 'david' 557 p.age = 16 558 print( r ) 559 560 print( r.tostring().decode() ) 561 562 print( r.person[1].name.pyval ) 563 564 r.dump( 'x.xml' ) 565