XMLToolkit 0,7,BETA.2

Petko Petkov <[email protected]>
Newsgroups gmane.comp.python.xml
Message-ID <[email protected]>
Hello to everybody,
This is XMLToolkit 0,7,BETA.2. I simplified most of the code. There are
a few new features added as well. Now you can use the Binding tool to
Bind XML documents to python objects.

doc = Binding.fromfile('GoogleService.wsdl')
doc.definitions.message
doc.definitions.message[0]
doc.definitions.message[1]
doc.definitions.message.name
doc.definitions.message.name = 'bla bla'
doc.definitions.message = '<h1>Hei I can assign XML to elements that
will be parsed on the fly.</h1> Good!'
doc.definitions.message[1] = OtherBinding
doc.definitions.message[2] = OtherElement
doc.definitions.message[0] = doc.definitions.message[2]
for m in doc.definitions.message:
    print m
    print m.name

doc.xml
doc.xml(encoding = 'utf-8')
doc.xml(encoding = 'utf-8', stream = mystream)
doc.xml(encoding = 'utf-8', file = 'myGoogle.wsdl')
str(doc.xml)
doc.xml.composestream(stream)

As you can see the syntax is similar  to Amara XMLToolkit.
I am still stuck with PEXComposer. If anyone is willing to help me out
with simplifying the code and writing proper Composer to assemble PEX
Objects back to XML, feel free to let me know. It shouldn't take that long.

I need your help more than ever. I believe that XT (XML Toolkit) is
coming nicely and I want to complete it as soon as possible because I
want to move forward and start doing something else. :) So, can you have
a look on the code and suggest any corrections or recommendations? Or
even help me out rewriting some of it. I believe that the current design
is not bad at all, however, somebody may suggest a different approach to
solve a problem.

Thanks.

_______________________________________________
XML-SIG maillist  -  [email protected]
http://mail.python.org/mailman/listinfo/xml-sig
xt.py (text/plain, 27.7 KB)
#    XML Toolkit
#    Copyright (C) 2005  Petko Petkov (GNUCITIZEN) [email protected]
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

__version__ = 0,7,'BETA.2'
__author__ = 'Petko Petkov'

__doc__ = """
XML Toolkit

XML Toolkit provides an easy to use, very pythonic approach to xml processing.
Why hard when it can be as simple as Python.
"""

import cStringIO

# XML PARSER

class Parser(object):
	"""
	Parser is responsible for parsing xml content. All methods are classmethods
	by design. This means that there is not need to instantiate the class.
	"""

	# ESCAPE FUNCTIONS

	@classmethod
	def escape(self, string, entities = {}):
		"""
		escape(string, entities = {}) -> escaped_string

		Replace special strings with their xml represenation. The optional
		entity dictionary is there if additional string substitutions are
		required.
		"""

		string = string.replace('&', '&amp;')
		string = string.replace('<', '&lt;')
		string = string.replace('>', '&gt;')

		for entity, value in entities.iteritems():
			string = string.replace(entity, value)

		return string

	@classmethod
	def unescape(self, string, entities = {}):
		"""
		unescape(string, entities = {}) -> unescaped_string

		Replace xml entities with their string representation. This method is
		a reverse of the escape method.
		"""

		string = string.replace('&gt;', '>')
		string = string.replace('&lt;', '<')
		string = string.replace('&amp;', '&')

		for entity, value in entities.iteritems():
			string = string.replace(value, entity)

		return string

	@classmethod
	def quote(self, string, entities = {}):
		"""
		quote(string, entities) -> quoted_string

		Replace special strings with their xml representation and quote. This
		function is useful when dealing with attributes. The optional entity
		dictionary is there if additional string substitutions are required.
		"""

		_entities = {}
		_entities.update(entities)
		_entities['"'] = '&quote;'
		_entities["'"] = '&apos;'

		return '"%s"' % self.escape(string, _entities)

	@classmethod
	def unquote(self, string, entities = {}):
		"""
		unquote(string, entities) -> unquoted_string

		Replace xml entities with their string representation. This method is a
		reverse of the unquote method.
		"""

		_entities = {}
		_entities.update(entities)
		_entities['"'] = '&quote;'
		_entities["'"] = '&apos;'

		string = string.strip()

		if string.startswith('"'):
			string = string.strip('"')

		else:
			string = string.strip("'")

		return self.unescape(string, _entities)

	# SPLIT FUNCTIONS

	@classmethod
	def splitdtd(self, dtdstring):
		"""
		splitdtd(dtdstring) -> dtdstring

		TODO: to be implemented
		"""

		return dtdstring

	@classmethod
	def splitcdata(self, cdatastring):
		"""
		splitcdata(cdatastring) -> string

		Remove the XML CDATA encapsulation.
		"""

		return cdatastring.lstrip('<![CDATA[').rstrip(']]>')

	@classmethod
	def splitcomment(self, commentstring):
		"""
		splitcomment(commentstring) -> string

		Remove the XML Comment encapsulation.
		"""

		return commentstring.lstrip('<!--').rstrip('-->').strip()

	@classmethod
	def splitpi(self, pistring):
		"""
		splitpi(pistring) -> target, content

		Remove the XML PI encapsulation.
		"""

		pistring = pistring.lstrip('<?').rstrip('?>').strip()

		try:
			space = pistring.index(' ')

		except:
			space = 0

		return pistring[:space], pistring[space:].strip()

	@classmethod
	def splitattributes(self, attributestring):
		"""
		splitattributes(attributestring) -> dict

		Remove the XML Attribute encapsulation and return a dictionary that
		maps attribute names to their coresponding values.
		"""

		string = attributestring.strip()

		if not string:
			return {}

		attributedict = {}

		while True:
			index = string.index('=')
			name = string[:index].strip()
			rest = string[index + 1:].strip()

			if rest.startswith('"'):
				end = rest[1:].index('"')

			else:
				end = rest[1:].index("'")

			value = self.unquote(rest[:end + 2])
			attributedict[name] = value

			string = rest[end + 2:].strip()

			if not string:
				break

		return attributedict

	@classmethod
	def splitelement(self, elementstring):
		"""
		splitelement(elementstring) -> qname, attributestring

		Remove the XML Element encapsulation and split to a tuple that contains
		the element name and the attributestring.
		"""

		element = elementstring.lstrip('<').rstrip('>')

		try:
			space = element.index(' ')

		except:
			space = len(element)

		qname = element[:space].strip('/').strip()
		attributestring = element[space:].rstrip('/').strip()

		return qname, attributestring

	@classmethod
	def splitstring(self, string):
		"""
		splitstring(string) -> generator

		Split string to XML Nodes.
		"""

		if not string:
			raise StopIteration

		while True:
			try:
				index = string.index('<')

			except:
				index = len(string)

			if string[:index]:
				end = index

			elif string.startswith('<![CDATA['):
				end = string.index(']]>') + 3

			elif string.startswith('<?'):
				end = string.index('?>') + 2

			elif string.startswith('<!DOCTYPE'):
				end = string.index(']>') + 2

			elif string.startswith('<!--'):
				end = string.index('-->') + 3

			else:
				end = string.index('>') + 1

			yield string[:end]
			string = string[end:]

			if not string:
				break

	@classmethod
	def splitstream(self, stream):
		"""
		splitstream(stream) -> generator

		Split stream to XML Nodes.
		
		TODO: implement stream parser instead of calling splitstring method
		"""

		return self.splitstring(stream.read())

	@classmethod
	def splitfile(self, filepath):
		"""
		splitfile(filepath) -> generator

		Split file to XML Nodes.
		"""

		file = open(filepath)
		generator = self.splitstream(file)
		file.close()

		return generator

	@classmethod
	def parsenodes(self, nodes, handler):
		"""
		parsenodes(nodes, handler) -> None

		Dispatch XML Nodes to their coresponding event handler.
		"""

		for node in nodes:
			if node.startswith('<![CDATA['):
				handler.cdata(self.splitcdata(node))

			elif node.startswith('<?'):
				handler.pi(*self.splitpi(node))

			elif node.startswith('<!DOCTYPE'):
				handler.dtd(*self.splitdtd(node))

			elif node.startswith('<!--'):
				handler.comment(self.splitcomment(node))

			elif node.startswith('</'):
				qname, attributes = self.splitelement(node)
				handler.endelement(qname)

			elif node.endswith('/>'):
				qname, attributes = self.splitelement(node)
				handler.beginelement(qname, self.splitattributes(attributes))
				handler.endelement(qname)

			elif node.startswith('<'):
				qname, attributes = self.splitelement(node)
				handler.beginelement(qname, self.splitattributes(attributes))

			else:
				handler.text(node)

	@classmethod
	def parsestream(self, stream, handler):
		"""
		parsestream(stream, handler) -> None

		Dispatch XML Nodes from stream to their coresponding event handler.
		"""

		self.parsenodes(self.splitstream(stream), handler)

	@classmethod
	def parsefile(self, filepath, handler):
		"""
		parsefile(filepath, handler) -> None

		Dispatch XML Nodes from file to their coresponding event handler.
		"""

		self.parsenodes(self.splitfile(filepath), handler)

	@classmethod
	def parsestring(self, string, handler):
		"""
		parsestring(string, handler) -> None

		Dispatch XML Nodes from string to their coresponding event handler.
		"""

		self.parsenodes(self.splitstring(string), handler)

# XML PARSER WITH NAMESPACE SUPPORT

class ParserNS(Parser):
	"""
	ParserNS extends Parser by adding namespace support.
	"""

	@classmethod
	def splitqname(self, qname):
		"""
		splitqname(qname) -> prefix, localName

		Split qualified name to prefix, localName tuple.
		"""

		try:
			index = qname.index(':')

		except:
			return '', qname

		return qname[:index], qname[index + 1:]

	@classmethod
	def splituname(self, uname):
		"""
		splituname(uname) -> namespace, localName

		Split universal name to namespace, localName tuple.
		"""

		try:
			index = uname.index('}')

			if not uname.startswith('{'):
				raise

		except:
			return '', uname

		return uname[1:index], uname[index + 1:]

	@classmethod
	def splitnamespaces(self, attributes):
		"""
		splitnamespaces(attributes) -> namespaces, attributes

		Separate namespace declarations from the attribute dictionary.
		"""

		namespacedict = {}
		attributedict = {}

		for name, value in attributes.iteritems():
			prefix, _name = self.splitqname(name)

			if prefix == 'xmlns':
				namespacedict[_name] = value

			elif prefix == '' and _name == 'xmlns':
				namespacedict[''] = value

			else:
				attributedict[name] = value

		return namespacedict, attributedict

	@classmethod
	def findnamespace(self, prefix, nslevels):
		"""
		findnamespace(prefix, nslevels) -> namespace

		Find namespace by prefix. This functions is a bit misleading. The
		nslevels dictionary contains level to namespaces dictionary mappings.
		The level represents the level at which a namespace declration is
		found.
		"""

		for index in reversed(nslevels.keys()):
			try:
				return nslevels[index][prefix]

			except:
				pass

		return ''

	@classmethod
	def rebuildattributes(self, attributes, nslevels):
		"""
		rebuildattributes(attributes, nslevels) -> qualified_attributes

		Rebuild attributes according to nslevels. The nslevels dictionary is
		used by the findnamespace method to find the coresponding namespace for
		each attribute.
		"""

		_attributes = {}

		for name, value in attributes.iteritems():
			prefix, name = self.splitqname(name)
			namespace = self.findnamespace(prefix, nslevels)
			_attributes[namespace, prefix, name] = value

		return _attributes

	@classmethod
	def parsenodes(self, nodes, handler):
		"""
		parsenodes(nodes, handler) -> None

		Dispatch XML Nodes to their coresponding event handler.
		"""

		namespaces = {}
		count = 0

		for node in nodes:
			if node.startswith('<![CDATA['):
				handler.cdata(self.splitcdata(node))

			elif node.startswith('<?'):
				handler.pi(*self.splitpi(node))

			elif node.startswith('<!DOCTYPE'):
				handler.dtd(self.splitdtd(node))

			elif node.startswith('<!--'):
				handler.comment(self.splitcomment(node))

			elif node.startswith('</'):
				count -= 1

				qname, attributes = self.splitelement(node)
				prefix, name = self.splitqname(qname)
				namespace = self.findnamespace(prefix, namespaces)

				if namespaces.has_key(count):
					del namespaces[count]

				handler.endelement((name, prefix, namespace))

			elif node.endswith('/>'):
				qname, attributes = self.splitelement(node)
				prefix, name = self.splitqname(qname)
				attributes = self.splitattributes(attributes)
				nsattributes, attributes = self.splitnamespaces(attributes)
				attributes = self.rebuildattributes(attributes, namespaces)

				if nsattributes:
					namespaces[count] = nsattributes

				namespace = self.findnamespace(prefix, namespaces)

				if namespaces.has_key(count):
					del namespaces[count]

				handler.beginelement((name, prefix, namespace), attributes)
				handler.endelement((name, prefix, namespace))

			elif node.startswith('<'):
				qname, attributes = self.splitelement(node)
				prefix, name = self.splitqname(qname)
				attributes = self.splitattributes(attributes)
				nsattributes, attributes = self.splitnamespaces(attributes)
				attributes = self.rebuildattributes(attributes, namespaces)

				if nsattributes:
					namespaces[count] = nsattributes

				namespace = self.findnamespace(prefix, namespaces)
				handler.beginelement((name, prefix, namespace), attributes)

				count += 1

			else:
				handler.text(node)

# CONTENT HANDLER

class Handler(object):
	"""
	Handle XML Events.
	"""

	def beginelement(self, qname, attributes):
		pass

	def endelement(self, qname):
		pass

	def dtd(self, content):
		pass

	def text(self, content):
		pass

	def cdata(self, content):
		pass

	def comment(self, content):
		pass

	def pi(self, target, content):
		pass

# PEX HANDLER, PARSER AND COMPOSER

class PEXHandler(Handler):
	"""
	Build PEX from ParserNS.
	"""

	def __init__(self, normalize = True):
		self.nodes = []
		self.prefixes = {}
		self.normalize = normalize
		self.elementque = [self]

	def beginelement(self, qname, attributes):
		name, prefix, namespace = qname
		self.prefixes[prefix] = namespace

		element = Element(name, namespace, prefix)

		for (namespace, prefix, name), value in attributes.iteritems():
			element.attributes.append(Attribute(
				name, value, namespace, prefix))

		self.elementque[-1].nodes.append(element)
		self.elementque.append(element)

	def endelement(self, qname):
		name, prefix, namespace = qname
		self.prefixes[prefix] = namespace

		self.elementque.pop()

	def dtd(self, content):
		self.elementque[-1].nodes.append(DTD(content))

	def cdata(self, content):
		self.elementque[-1].nodes.append(CDATA(content))

	def comment(self, content):
		self.elementque[-1].nodes.append(Comment(content))

	def pi(self, target, content):
		self.elementque[-1].nodes.append(PI(target, content))

	def text(self, content):
		if self.normalize:
			content = content.strip()

			if content:
				self.elementque[-1].nodes.append(Text(content))

		else:
			self.elementque[-1].nodes.append(Text(content))

class PEXParser(object):
	"""
	Build PEX from PEXHandler and ParserNS.
	"""

	@classmethod
	def parsestring(self, string, normalize = True):
		"""
		parsestring(string, normalize = True) -> []

		Parse string and return nodes as list.
		"""

		handler = PEXHandler(normalize)
		ParserNS.parsestring(string, handler)
		return handler.nodes

	@classmethod
	def parsestream(self, stream, normalize = True):
		"""
		parsestream(stream, normalize = True) -> []

		Parse stream and return nodes as list.
		"""

		handler = PEXHandler(normalize)
		ParserNS.parsestream(stream, handler)
		return handler.nodes

	@classmethod
	def parsefile(self, filepath, normalize = True):
		"""
		parsefile(filepath, normalize = True) -> []

		Parse file and return nodes as list.
		"""

		handler = PEXHandler(normalize)
		ParserNS.parsefile(filepath, handler)
		return handler.nodes

class PEXComposer(object):
	"""
	Compose XML from PEX.
	"""

	@classmethod
	def findprefix(self, namespace, namespaces):
		"""
		findprefix(namespace, namespaces) -> prefix

		Find prefix from namespace in namespaces. Otherwise, return None.
		"""

		for key, value in namespaces.iteritems():
			if value == namespace:
				return key

	@classmethod
	def findnamespace(self, prefix, namespaces):
		"""
		findnamespace(prefix, namespaces) -> namespace

		Find namespace from prefix in namespaces. Otherwise, return None.
		"""

		try:
			return namespaces[prefix]

		except:
			pass

	@classmethod
	def addnamespace(self, namespace, prefix, namespaces):
		"""
		addnamespace(namespace, prefix, namespaces) -> None

		Add prefix to namespace mapping in namespaces as such all prefix names
		are resolved automatically. 
		"""

		if namespaces.has_key(prefix):
			if namespaces[prefix] != namespace:
				index = 0
				prefix = 'ns0'

				while namespaces.has_key(prefix):
					index = index + 1
					prefix = 'ns' + str(index)

				namespaces[prefix] = namespace

		else:
			_prefix = self.findprefix(namespace, namespaces)

			if _prefix is None:
				namespaces[prefix] = namespace

	@classmethod
	def extractnamespaces(self, list, namespaces = {}):
		"""
		extractnamespaces(list) -> {}

		Extract namespaces from nodes and return prefix to namespaces mappins
		in a dictionary. list must contain Element and Attributes only. The
		optional namespaces parameter supplies base namespaces.
		"""

		_namespaces = {}
		_namespaces.update(namespaces)

		for item in list:
			if not item.namespace:
				continue

			prefix = item.prefix or 'ns0'
			self.addnamespace(item.namespace, prefix, _namespaces)

		return _namespaces

	@classmethod
	def exportnode(self, node, stream, namespaces = {}):
		"""
		exportnode(node, stream, namespaces = {}) -> None

		Export node into stream. The namespaces parameter will be modified.
		Exported elements does not contain xml declrations.
		"""

		if isinstance(node, PI):
			stream.write('<?%s %s ?>' % (node.target or '', node.content))

		elif isinstance(node, DTD):
			stream.write(node.content)

		elif isinstance(node, CDATA):
			stream.write('<![CDATA['+ node +']>')

		elif isinstance(node, Comment):
			stream.write('<!-- ' + node + ' -->')

		elif isinstance(node, (Text, str, unicode)):
			stream.write(node)

		elif isinstance(node, Element):
			pass

		else:
			raise ValueError, 'TODO: add message here'

	@classmethod
	def exportnodens(self, node, stream, namespaces = {}):
		"""
		exportnodens(node, stream, namespaces = {} -> None

		Export node into stream. The namespaces parameter will be modified.
		Exported element contain namespace declarations.
		"""

		if not isinstance(node, Element):
			self.exportnode(node, stream, namespaces)

		else:
			namespaces = self.extractnamespaces(
				[node] + node.attributes,
				namespaces)

			buff = cStringIO.StringIO()

			for _node in node.nodes:
				self.exportnode(_node, buff, namespaces)

			attributes = ''

			for attribute in node.attributes:
				attributes = '%s %s=%s' % (
					attributes,
					attribute.name,
					ParserNS.quote(attribute.value))

			for prefix, namespace in namespaces.iteritems():
				attributes = '%s %s=%s' % (
					attributes,
					'xmlns:' + prefix,
					namespace)

			prefix = self.findprefix(node.namespace, namespaces)
			name = '%s:%s' % (prefix, node.name)

			if not node.nodes:
				stream.write('<%s%s/>' % (name, attributes))

			else:
				stream.write('<%s%s>' % (name, attributes))
				stream.write(buff.getvalue())
				stream.write('<%s>' % name)

	@classmethod
	def composestream(self, pex, stream, encoding = 'utf-8', namespaces = {}):
		"""
		composestream(pex, stream, encoding = 'utf-8', namespaces = {}) -> None

		Compose XML document from pex and write to stream. pex is either
		Element or list.
		"""

		class encapsulate(object):
			def __init__(self, stream): self.stream = stream
			def write(self, string): self.stream.write(string.encode(encoding))

		stream = encapsulate(stream)
		stream.write('<?xml version="1.0" encoding="%s"?>\n' % encoding)

		if isinstance(pex, list):
			for node in pex:
				if isinstance(node, PI) and node.target == 'xml':
					continue

				self.exportnodens(node, stream, namespaces)

		else:
			self.exportnodens(pex, stream, namespaces)

	@classmethod
	def composestring(self, pex, encoding = 'utf-8', namespaces = {}):
		"""
		composestring(pex, encoding = 'utf-8', namespaces = {}) -> string

		Compose XML document from pex and return string. pex is either Element
		or list.
		"""

		stream = cStringIO.StringIO()
		self.composestream(pex, stream, encoding, namespaces)
		return stream.getvalue()

	@classmethod
	def composefile(self, pex, filepath, encoding = 'utf-8', namespaces = {}):
		"""
		composefile(pex, filepath, encoding = 'utf-8', namespaces = {}) -> None

		Compose XML document from pex and write to file. pex is either Element
		or list.
		"""

		file = open(filepath, 'w')
		self.composestream(pex, file, encoding, namespaces)
		file.close()

# PEX TYPES

class Text(unicode):
	"""
	XML Text node
	"""

class CDATA(unicode):
	"""
	XML CDATA node
	"""

class Comment(unicode):
	"""
	XML Comment node
	"""

class PI(object):
	"""
	XML ProcessInstruction node
	"""

	def __init__(self, target = '', content = ''):
		self.target = target
		self.content = content

class DTD(object):
	"""
	XML DTD node

	NOTE: not implemented
	"""

	def __init__(self, content):
		self.content = content

class Attribute(object):
	"""
	XML Attribute
	"""

	def __init__(self, name, value = '', namespace = '', prefix = ''):
		self.name = name
		self.value = value
		self.namespace = namespace
		self.prefix = prefix

class Element(object):
	"""
	XML Element
	"""

	def __init__(self, name, namespace = '', prefix = ''):
		self.name = name
		self.namespace = namespace
		self.prefix = prefix

		self.nodes = []
		self.attributes = []

# PEX HELPERS

class Composer(PEXComposer):
	"""
	Compose XML from Binding
	"""

	def __init__(self, binding, encoding = 'utf-8', namespaces = {}):
		self.encoding = encoding
		self.namespaces = namespaces

		if isinstance(binding, Binding):
			if binding.xml_name == '__binding__':
				self.element = binding.xml_nodes

			else:
				self.element = binding.xml_element

		else:
			self.element = binding

	def __call__(self, encoding = None, namespaces = None, stream = None,
	             file = None):

		if encoding is None:
			encoding = self.encoding

		if namespaces is None:
			namespaces = self.namespaces
		
		if stream is not None:
			self.composestream(self.element, stream, encoding, namespaces)

		elif file is not None:
			self.composefile(self.element, file, encoding, namespaces)

		else:
			return self.composestring(self.element, encoding, namespaces)

	def __str__(self):
		return self.composestring(self.element, self.encoding, self.namespaces)

class Binding(object):
	"""
	XML to Python object Binding
	"""

	def __init__(self, element, origin = None):
		self.xml_element = element
		self.xml_origin = origin
		self.xml = Composer(element)

	def __getattr__(self, name):
		if not name.startswith('xml'):
			for attribute in self.xml_element.attributes:
				if attribute.name == name:
					return attribute.value

			for node in self.xml_element.nodes:
				if isinstance(node, Element) and node.name == name:
					return Binding(node, self.xml_element)

		else:
			if name not in ('xml_element', 'xml_origin'):
				return getattr(self.xml_element, name[4:])

		raise AttributeError, 'TODO: add message here'

	def __setattr__(self, name, value):
		if not name.startswith('xml'):
			for attribute in self.xml_element.attributes:
				if attribute.name == name:
					attribute.value = value
					return

			for node in self.xml_element.nodes:
				if isinstance(node, Element) and node.name == name:
					return self.__setelement_(node, value)

			self.xml_element.attributes.append(Attribute(
				name = name,
				value = value))

			return

		object.__setattr__(self, name, value)

	def __delattr__(self, name):
		if not name.startswith('xml'):
			for index, attribute in enumerate(self.xml_element.attributes):
				if attribute.name == name:
					del self.xml_element.attributes[index]
					return

			for index, node in enumerate(self.xml_element.nodes):
				if isinstance(node, Element) and node.name == name:
					del self.xml_elements.nodes[index]
					return

		raise AttributeError, 'TODO: add message here'

	def __getitem__(self, key):
		if isinstance(key, slice):
			# TODO: support for slices
			pass

		elif isinstance(key, int):
			if self.xml_origin is None:
				raise RuntimeError, 'TODO: add message here'

			if key >= 0:
				nodes = lambda: self.xml_origin.nodes

			else:
				nodes = lambda: reversed(self.xml_origin.nodes)
				key = key * -1 - 1

			for node in nodes():
				if isinstance(node, Element) \
				and node.name == self.xml_element.name:
					if key == 0:
						return Binding(node)

					key = key - 1

			raise IndexError, 'TODO: add message here'

		elif isinstance(key, (str, unicode)):
			matches = self.__makematch_(key)

			if key.startswith('@'):
				for attribute in self.xml_element.attributes:
					if matches(attribute):
						return attribute.value

			else:
				for node in self.xml_element.nodes:
					if matches(node):
						return Binding(node, self.xml_element)

		raise KeyError, 'TODO: add message here'

	def __setitem__(self, key, value):
		if isinstance(key, slice):
			# TODO: support for slices
			pass

		elif isinstance(key, int):
			if self.xml_origin is None:
				raise RuntimeError, 'TODO: add message here'

			if key >= 0:
				nodes = lambda: self.xml_origin.nodes

			else:
				nodes = lambda: reversed(self.xml_origin.nodes)
				key = key * -1 - 1

			for node in nodes():
				if isinstance(node, Element) \
				and node.name == self.xml_element.name:
					if key == 0:
						return self.__setelement_(node, value)

					key = key - 1

			raise IndexError, 'TODO: add message here'

		elif isinstance(key, (str, unicode)):
			matches = self.__makematch_(key)

			if key.startswith('@'):
				for attribute in self.xml_element.attributes:
					if matches(attribute):
						attribute.value = value
						return

			else:
				for node in self.xml_element.nodes:
					if matches(node):
						return self.__setelement_(node, value)

		raise KeyError, 'TODO: add message here'

	def __delitem__(self, key):
		if isinstance(key, slice):
			# TODO: support for slices
			pass

		elif isinstance(key, int):
			if self.xml_origin is None:
				raise RuntimeError, 'TODO: add message here'

			if key >= 0:
				nodes = lambda: self.xml_origin.nodes

			else:
				nodes = lambda: reversed(self.xml_origin.nodes)
				key = key * -1 - 1

			for index, node in enumerate(nodes()):
				if isinstance(node, Element) \
				and node.name == self.xml_element.name:
					if key == 0:
						del self.xml_origin.nodes[index]
						return

					key = key - 1

			raise IndexError, 'TODO: add message here'

		elif isinstance(key, (str, unicode)):
			matches = self.__makematch_(key)

			if key.startswith('@'):
				for index, attribute in enumerate(self.xml_element.attributes):
					if matches(attribute):
						del self.xml_element.attributes[index]
						return

			else:
				for index, node in enumerate(self.xml_element.nodes):
					if matches(node):
						del self.xml_element.nodes[index]
						return

		raise KeyError, 'TODO: add message here'

	def __len__(self):
		if self.xml_origin is None:
			raise RuntimeError, 'TODO: add message here'

		len = 0

		for node in self.xml_origin.nodes:
			if isinstance(node, Element) \
			and node.name == self.xml_element.name:
				len = len + 1

		return len

	def __iter__(self):
		if self.xml_origin is None:
			raise RuntimeError, 'TODO: add message here'

		def iterator():
			for node in self.xml_origin.nodes:
				if isinstance(node, Element) \
				and node.name == self.xml_element.name:
					yield Binding(node)

		return iterator()

	def __repr__(self):
		return PEXComposer.composestring(self.xml_element.nodes)

	def __setelement_(self, element, value):
		"""
		__setelement_(element, value) -> None

		Set element content to value. value is string, Binding or another
		Element.
		"""

		if isinstance(value, (str, unicode)):
			element.nodes = PEXParser.parsestring(value)

		elif isinstance(value, Binding):
			node.name = value.xml_name
			node.namespace = value.xml_namespace
			node.prefix = value.xml_prefix
			node.attributes = value.xml_attributes
			node.nodes = value.xml_nodes

		elif isinstance(value, Element):
			node.name = value.name
			node.namespace = value.namespace
			node.prefix = value.prefix
			node.attributes = value.attributes
			node.nodes = value.nodes

		else:
			raise ValueError, 'TODO: add message here'

	@classmethod
	def fromstring(self, string, normalize = True):
		"""
		fromstring(string, normalize = True) -> Binding

		Parse string and return Binding.
		"""

		element = Element('__binding__')
		element.nodes = PEXParser.parsestring(string, normalize)
		return Binding(element)

	@classmethod
	def fromstream(self, stream, normalize = True):
		"""
		fromstream(stream, normalize = True) -> Binding

		Parse stream and return Binding.
		"""

		element = Element('__binding__')
		element.nodes = PEXParser.parsestream(stream, normalize)
		return Binding(element)

	@classmethod
	def fromfile(self, filepath, normalize = True):
		"""
		fromfile(filepath, normalize = True) -> Binding

		Parse file and return Binding.
		"""

		element = Element('__binding__')
		element.nodes = PEXParser.parsefile(filepath, normalize)
		return Binding(element)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.