diff --git a/src/common/xmpp/protocol.py b/src/common/xmpp/protocol.py index ff19678d8..17e2096a3 100644 --- a/src/common/xmpp/protocol.py +++ b/src/common/xmpp/protocol.py @@ -476,7 +476,7 @@ class Protocol(Node): self.setTo(self['to']) if self['from']: self.setFrom(self['from']) - if node and type(self )== type(node) and self.__class__ == node.__class__ and self.attrs.has_key('id'): + if node and type(self) == type(node) and self.__class__ == node.__class__ and self.attrs.has_key('id'): del self.attrs['id'] self.timestamp = None for d in self.getTags('delay', namespace=NS_DELAY2): diff --git a/src/common/xmpp/proxy_connectors.py b/src/common/xmpp/proxy_connectors.py index e4fb4dbd8..b4e7acfcd 100644 --- a/src/common/xmpp/proxy_connectors.py +++ b/src/common/xmpp/proxy_connectors.py @@ -14,27 +14,30 @@ ## 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. -''' -Module containing classes for proxy connecting. So far its HTTP CONNECT -and SOCKS5 proxy. + +""" +Module containing classes for proxy connecting. So far its HTTP CONNECT and +SOCKS5 proxy + Authentication to NTLM (Microsoft implementation) proxies can be next. -''' +""" import struct, socket, base64 import logging log = logging.getLogger('gajim.c.x.proxy_connectors') class ProxyConnector: - ''' + """ Interface for proxy-connecting object - when tunnneling XMPP over proxies, - some connecting process usually has to be done before opening stream. - Proxy connectors are used right after TCP connection is estabilished. - ''' + some connecting process usually has to be done before opening stream. Proxy + connectors are used right after TCP connection is estabilished + """ + def __init__(self, send_method, onreceive, old_on_receive, on_success, - on_failure, xmpp_server, proxy_creds=(None,None)): - ''' + on_failure, xmpp_server, proxy_creds=(None,None)): + """ Creates proxy connector, starts connecting immediately and gives control - back to transport afterwards. + back to transport afterwards :param send_method: transport send method :param onreceive: method to set on_receive callbacks @@ -44,7 +47,7 @@ class ProxyConnector: :param on_failure: called when errors occured while connecting :param xmpp_server: tuple of (hostname, port) :param proxy_creds: tuple of (proxy_user, proxy_credentials) - ''' + """ self.send = send_method self.onreceive = onreceive self.old_on_receive = old_on_receive @@ -58,12 +61,12 @@ class ProxyConnector: @classmethod def get_instance(cls, *args, **kwargs): - ''' - Factory Method for object creation. + """ + Factory Method for object creation - Use this instead of directly initializing the class in order to make - unit testing much easier. - ''' + Use this instead of directly initializing the class in order to make unit + testing much easier. + """ return cls(*args, **kwargs) def start_connecting(self): @@ -75,11 +78,11 @@ class ProxyConnector: class HTTPCONNECTConnector(ProxyConnector): def start_connecting(self): - ''' - Connects to proxy, supplies login and password to it - (if were specified while creating instance). Instructs proxy to make - connection to the target server. - ''' + """ + Connect to a proxy, supply login and password to it (if were specified + while creating instance). Instruct proxy to make connection to the target + server. + """ log.info('Proxy server contacted, performing authentification') connector = ['CONNECT %s:%s HTTP/1.1' % self.xmpp_server, 'Proxy-Connection: Keep-Alive', @@ -115,10 +118,11 @@ class HTTPCONNECTConnector(ProxyConnector): class SOCKS5Connector(ProxyConnector): - ''' + """ SOCKS5 proxy connection class. Allows to use SOCKS5 proxies with - (optionally) simple authentication (only USERNAME/PASSWORD auth). - ''' + (optionally) simple authentication (only USERNAME/PASSWORD auth) + """ + def start_connecting(self): log.info('Proxy server contacted, performing authentification') if self.proxy_user and self.proxy_pass: diff --git a/src/common/xmpp/roster_nb.py b/src/common/xmpp/roster_nb.py index 715476b9e..bf5600cb7 100644 --- a/src/common/xmpp/roster_nb.py +++ b/src/common/xmpp/roster_nb.py @@ -16,10 +16,11 @@ # $Id: roster.py,v 1.17 2005/05/02 08:38:49 snakeru Exp $ -''' + +""" Simple roster implementation. Can be used though for different tasks like mass-renaming of contacts. -''' +""" from protocol import JID, Iq, Presence, Node, NodeProcessed, NS_MUC_USER, NS_ROSTER from plugin import PlugIn @@ -29,15 +30,18 @@ log = logging.getLogger('gajim.c.x.roster_nb') class NonBlockingRoster(PlugIn): - ''' Defines a plenty of methods that will allow you to manage roster. - Also automatically track presences from remote JIDs taking into - account that every JID can have multiple resources connected. Does not - currently support 'error' presences. - You can also use mapping interface for access to the internal representation of - contacts in roster. - ''' + """ + Defines a plenty of methods that will allow you to manage roster. Also + automatically track presences from remote JIDs taking into account that + every JID can have multiple resources connected. Does not currently support + 'error' presences. You can also use mapping interface for access to the + internal representation of contacts in roster + """ + def __init__(self, version=''): - ''' Init internal variables. ''' + """ + Init internal variables + """ PlugIn.__init__(self) self.version = version self._data = {} @@ -45,11 +49,15 @@ class NonBlockingRoster(PlugIn): self._exported_methods=[self.getRoster] self.received_from_server = False - def Request(self,force=0): - ''' Request roster from server if it were not yet requested - (or if the 'force' argument is set). ''' - if self.set is None: self.set=0 - elif not force: return + def Request(self, force=0): + """ + Request roster from server if it were not yet requested (or if the + 'force' argument is set) + """ + if self.set is None: + self.set = 0 + elif not force: + return iq = Iq('get',NS_ROSTER) iq.setTagAttr('query', 'ver', self.version) @@ -59,9 +67,11 @@ class NonBlockingRoster(PlugIn): log.info('Roster requested from server') return id_ - def RosterIqHandler(self,dis,stanza): - ''' Subscription tracker. Used internally for setting items state in - internal roster representation. ''' + def RosterIqHandler(self, dis, stanza): + """ + Subscription tracker. Used internally for setting items state in internal + roster representation + """ sender = stanza.getAttr('from') if not sender is None and not sender.bareMatch( self._owner.User + '@' + self._owner.Server): @@ -91,9 +101,11 @@ class NonBlockingRoster(PlugIn): # Looks like we have a workaround # raise NodeProcessed # a MUST. Otherwise you'll get back an - def PresenceHandler(self,dis,pres): - ''' Presence tracker. Used internally for setting items' resources state in - internal roster representation. ''' + def PresenceHandler(self, dis, pres): + """ + Presence tracker. Used internally for setting items' resources state in + internal roster representation + """ if pres.getTag('x', namespace=NS_MUC_USER): return jid=pres.getFrom() @@ -118,112 +130,204 @@ class NonBlockingRoster(PlugIn): elif typ=='unavailable' and item['resources'].has_key(jid.getResource()): del item['resources'][jid.getResource()] # Need to handle type='error' also - def _getItemData(self,jid,dataname): - ''' Return specific jid's representation in internal format. Used internally. ''' - jid=jid[:(jid+'/').find('/')] + def _getItemData(self, jid, dataname): + """ + Return specific jid's representation in internal format. Used internally + """ + jid = jid[:(jid+'/').find('/')] return self._data[jid][dataname] - def _getResourceData(self,jid,dataname): - ''' Return specific jid's resource representation in internal format. Used internally. ''' - if jid.find('/')+1: - jid,resource=jid.split('/',1) - if self._data[jid]['resources'].has_key(resource): return self._data[jid]['resources'][resource][dataname] - elif self._data[jid]['resources'].keys(): - lastpri=-129 - for r in self._data[jid]['resources'].keys(): - if int(self._data[jid]['resources'][r]['priority'])>lastpri: resource,lastpri=r,int(self._data[jid]['resources'][r]['priority']) - return self._data[jid]['resources'][resource][dataname] - def delItem(self,jid): - ''' Delete contact 'jid' from roster.''' - self._owner.send(Iq('set',NS_ROSTER,payload=[Node('item',{'jid':jid,'subscription':'remove'})])) - def getAsk(self,jid): - ''' Returns 'ask' value of contact 'jid'.''' - return self._getItemData(jid,'ask') - def getGroups(self,jid): - ''' Returns groups list that contact 'jid' belongs to.''' - return self._getItemData(jid,'groups') - def getName(self,jid): - ''' Returns name of contact 'jid'.''' - return self._getItemData(jid,'name') - def getPriority(self,jid): - ''' Returns priority of contact 'jid'. 'jid' should be a full (not bare) JID.''' - return self._getResourceData(jid,'priority') - def getRawRoster(self): - ''' Returns roster representation in internal format. ''' - return self._data - def getRawItem(self,jid): - ''' Returns roster item 'jid' representation in internal format. ''' - return self._data[jid[:(jid+'/').find('/')]] - def getShow(self, jid): - ''' Returns 'show' value of contact 'jid'. 'jid' should be a full (not bare) JID.''' - return self._getResourceData(jid,'show') - def getStatus(self, jid): - ''' Returns 'status' value of contact 'jid'. 'jid' should be a full (not bare) JID.''' - return self._getResourceData(jid,'status') - def getSubscription(self,jid): - ''' Returns 'subscription' value of contact 'jid'.''' - return self._getItemData(jid,'subscription') - def getResources(self,jid): - ''' Returns list of connected resources of contact 'jid'.''' - return self._data[jid[:(jid+'/').find('/')]]['resources'].keys() - def setItem(self,jid,name=None,groups=[]): - ''' Renames contact 'jid' and sets the groups list that it now belongs to.''' - iq=Iq('set',NS_ROSTER) - query=iq.getTag('query') - attrs={'jid':jid} - if name: attrs['name']=name - item=query.setTag('item',attrs) - for group in groups: item.addChild(node=Node('group',payload=[group])) - self._owner.send(iq) - def setItemMulti(self,items): - ''' Renames multiple contacts and sets their group lists.''' - iq=Iq('set',NS_ROSTER) - query=iq.getTag('query') - for i in items: - attrs={'jid':i['jid']} - if i['name']: attrs['name']=i['name'] - item=query.setTag('item',attrs) - for group in i['groups']: item.addChild(node=Node('group',payload=[group])) - self._owner.send(iq) - def getItems(self): - ''' Return list of all [bare] JIDs that the roster is currently tracks.''' - return self._data.keys() - def keys(self): - ''' Same as getItems. Provided for the sake of dictionary interface.''' - return self._data.keys() - def __getitem__(self,item): - ''' Get the contact in the internal format. Raises KeyError if JID 'item' is not in roster.''' - return self._data[item] - def getItem(self,item): - ''' Get the contact in the internal format (or None if JID 'item' is not in roster).''' - if self._data.has_key(item): return self._data[item] - def Subscribe(self,jid): - ''' Send subscription request to JID 'jid'.''' - self._owner.send(Presence(jid,'subscribe')) - def Unsubscribe(self,jid): - ''' Ask for removing our subscription for JID 'jid'.''' - self._owner.send(Presence(jid,'unsubscribe')) - def Authorize(self,jid): - ''' Authorise JID 'jid'. Works only if these JID requested auth previously. ''' - self._owner.send(Presence(jid,'subscribed')) - def Unauthorize(self,jid): - ''' Unauthorise JID 'jid'. Use for declining authorisation request - or for removing existing authorization. ''' - self._owner.send(Presence(jid,'unsubscribed')) - def getRaw(self): - '''Returns the internal data representation of the roster.''' - return self._data - def setRaw(self, data): - '''Returns the internal data representation of the roster.''' - self._data = data - self._data[self._owner.User+'@'+self._owner.Server]={'resources':{},'name':None,'ask':None,'subscription':None,'groups':None,} - self.set=1 - # copypasted methods for roster.py from constructor to here + def _getResourceData(self, jid, dataname): + """ + Return specific jid's resource representation in internal format. Used + internally + """ + if jid.find('/') + 1: + jid, resource = jid.split('/', 1) + if self._data[jid]['resources'].has_key(resource): + return self._data[jid]['resources'][resource][dataname] + elif self._data[jid]['resources'].keys(): + lastpri = -129 + for r in self._data[jid]['resources'].keys(): + if int(self._data[jid]['resources'][r]['priority']) > lastpri: + resource,lastpri=r,int(self._data[jid]['resources'][r]['priority']) + return self._data[jid]['resources'][resource][dataname] + + def delItem(self, jid): + """ + Delete contact 'jid' from roster + """ + self._owner.send(Iq('set', NS_ROSTER, payload=[Node('item', {'jid': jid, 'subscription': 'remove'})])) + + def getAsk(self, jid): + """ + Return 'ask' value of contact 'jid' + """ + return self._getItemData(jid, 'ask') + + def getGroups(self, jid): + """ + Return groups list that contact 'jid' belongs to + """ + return self._getItemData(jid, 'groups') + + def getName(self, jid): + """ + Return name of contact 'jid' + """ + return self._getItemData(jid, 'name') + + def getPriority(self, jid): + """ + Return priority of contact 'jid'. 'jid' should be a full (not bare) JID + """ + return self._getResourceData(jid, 'priority') + + def getRawRoster(self): + """ + Return roster representation in internal format + """ + return self._data + + def getRawItem(self, jid): + """ + Return roster item 'jid' representation in internal format + """ + return self._data[jid[:(jid+'/').find('/')]] + + def getShow(self, jid): + """ + Return 'show' value of contact 'jid'. 'jid' should be a full (not bare) + JID + """ + return self._getResourceData(jid, 'show') + + def getStatus(self, jid): + """ + Return 'status' value of contact 'jid'. 'jid' should be a full (not bare) + JID + """ + return self._getResourceData(jid, 'status') + + def getSubscription(self, jid): + """ + Return 'subscription' value of contact 'jid' + """ + return self._getItemData(jid, 'subscription') + + def getResources(self, jid): + """ + Return list of connected resources of contact 'jid' + """ + return self._data[jid[:(jid+'/').find('/')]]['resources'].keys() + + def setItem(self, jid, name=None, groups=[]): + """ + Rename contact 'jid' and sets the groups list that it now belongs to + """ + iq = Iq('set',NS_ROSTER) + query = iq.getTag('query') + attrs = {'jid': jid} + if name: + attrs['name'] = name + item = query.setTag('item' ,attrs) + for group in groups: + item.addChild(node=Node('group', payload=[group])) + self._owner.send(iq) + + def setItemMulti(self, items): + """ + Rename multiple contacts and sets their group lists + """ + iq = Iq('set', NS_ROSTER) + query = iq.getTag('query') + for i in items: + attrs = {'jid': i['jid']} + if i['name']: + attrs['name'] = i['name'] + item = query.setTag('item', attrs) + for group in i['groups']: + item.addChild(node=Node('group', payload=[group])) + self._owner.send(iq) + + def getItems(self): + """ + Return list of all [bare] JIDs that the roster is currently tracks + """ + return self._data.keys() + + def keys(self): + """ + Same as getItems. Provided for the sake of dictionary interface + """ + return self._data.keys() + + def __getitem__(self, item): + """ + Get the contact in the internal format. Raises KeyError if JID 'item' is + not in roster + """ + return self._data[item] + + def getItem(self,item): + """ + Get the contact in the internal format (or None if JID 'item' is not in + roster) + """ + if self._data.has_key(item): + return self._data[item] + + def Subscribe(self, jid): + """ + Send subscription request to JID 'jid' + """ + self._owner.send(Presence(jid, 'subscribe')) + + def Unsubscribe(self,jid): + """ + Ask for removing our subscription for JID 'jid' + """ + self._owner.send(Presence(jid, 'unsubscribe')) + + def Authorize(self, jid): + """ + Authorize JID 'jid'. Works only if these JID requested auth previously + """ + self._owner.send(Presence(jid, 'subscribed')) + + def Unauthorize(self, jid): + """ + Unauthorise JID 'jid'. Use for declining authorisation request or for + removing existing authorization + """ + self._owner.send(Presence(jid, 'unsubscribed')) + + def getRaw(self): + """ + Return the internal data representation of the roster + """ + return self._data + + def setRaw(self, data): + """ + Return the internal data representation of the roster + """ + self._data = data + self._data[self._owner.User + '@' + self._owner.Server] = { + 'resources': {}, + 'name': None, + 'ask': None, + 'subscription': None, + 'groups': None + } + self.set = 1 def plugin(self, owner, request=1): - ''' Register presence and subscription trackers in the owner's dispatcher. - Also request roster from server if the 'request' argument is set. - Used internally.''' + """ + Register presence and subscription trackers in the owner's dispatcher. + Also request roster from server if the 'request' argument is set. Used + internally + """ self._owner.RegisterHandler('iq', self.RosterIqHandler, 'result', NS_ROSTER, makefirst = 1) self._owner.RegisterHandler('iq', self.RosterIqHandler, 'set', NS_ROSTER) self._owner.RegisterHandler('presence', self.PresenceHandler) @@ -242,7 +346,9 @@ class NonBlockingRoster(PlugIn): return True def getRoster(self, on_ready=None, force=False): - ''' Requests roster from server if neccessary and returns self. ''' + """ + Request roster from server if neccessary and returns self + """ return_self = True if not self.set: self.on_ready = on_ready diff --git a/src/common/xmpp/simplexml.py b/src/common/xmpp/simplexml.py index f47142c46..2ace6657a 100644 --- a/src/common/xmpp/simplexml.py +++ b/src/common/xmpp/simplexml.py @@ -14,64 +14,96 @@ # $Id: simplexml.py,v 1.27 2005/04/30 07:20:27 snakeru Exp $ -'''Simplexml module provides xmpppy library with all needed tools to handle XML nodes and XML streams. -I'm personally using it in many other separate projects. It is designed to be as standalone as possible.''' +""" +Simplexml module provides xmpppy library with all needed tools to handle XML +nodes and XML streams. I'm personally using it in many other separate +projects. It is designed to be as standalone as possible +""" import xml.parsers.expat import logging log = logging.getLogger('gajim.c.x.simplexml') def XMLescape(txt): - '''Returns provided string with symbols & < > " replaced by their respective XML entities.''' + """ + Return provided string with symbols & < > " replaced by their respective XML + entities + """ # replace also FORM FEED and ESC, because they are not valid XML chars return txt.replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """).replace(u'\x0C', "").replace(u'\x1B', "") ENCODING='utf-8' + def ustr(what): - '''Converts object "what" to unicode string using it's own __str__ method if accessible or unicode method otherwise.''' - if isinstance(what, unicode): return what - try: r=what.__str__() - except AttributeError: r=str(what) - if not isinstance(r, unicode): return unicode(r,ENCODING) + """ + Converts object "what" to unicode string using it's own __str__ method if + accessible or unicode method otherwise + """ + if isinstance(what, unicode): + return what + try: + r = what.__str__() + except AttributeError: + r = str(what) + if not isinstance(r, unicode): + return unicode(r, ENCODING) return r class Node(object): - ''' Node class describes syntax of separate XML Node. It have a constructor that permits node creation - from set of "namespace name", attributes and payload of text strings and other nodes. - It does not natively support building node from text string and uses NodeBuilder class for that purpose. - After creation node can be mangled in many ways so it can be completely changed. - Also node can be serialised into string in one of two modes: default (where the textual representation - of node describes it exactly) and "fancy" - with whitespace added to make indentation and thus make - result more readable by human. + """ + Node class describes syntax of separate XML Node. It have a constructor that + permits node creation from set of "namespace name", attributes and payload + of text strings and other nodes. It does not natively support building node + from text string and uses NodeBuilder class for that purpose. After + creation node can be mangled in many ways so it can be completely changed. + Also node can be serialised into string in one of two modes: default (where + the textual representation of node describes it exactly) and "fancy" - with + whitespace added to make indentation and thus make result more readable by + human. - Node class have attribute FORCE_NODE_RECREATION that is defaults to False thus enabling fast node - replication from the some other node. The drawback of the fast way is that new node shares some - info with the "original" node that is changing the one node may influence the other. Though it is - rarely needed (in xmpppy it is never needed at all since I'm usually never using original node after - replication (and using replication only to move upwards on the classes tree). - ''' - FORCE_NODE_RECREATION=0 - def __init__(self, tag=None, attrs={}, payload=[], parent=None, nsp=None, node_built=False, node=None): - ''' Takes "tag" argument as the name of node (prepended by namespace, if needed and separated from it - by a space), attrs dictionary as the set of arguments, payload list as the set of textual strings - and child nodes that this node carries within itself and "parent" argument that is another node - that this one will be the child of. Also the __init__ can be provided with "node" argument that is - either a text string containing exactly one node or another Node instance to begin with. If both - "node" and other arguments is provided then the node initially created as replica of "node" - provided and then modified to be compliant with other arguments.''' + Node class have attribute FORCE_NODE_RECREATION that is defaults to False + thus enabling fast node replication from the some other node. The drawback + of the fast way is that new node shares some info with the "original" node + that is changing the one node may influence the other. Though it is rarely + needed (in xmpppy it is never needed at all since I'm usually never using + original node after replication (and using replication only to move upwards + on the classes tree). + """ + + FORCE_NODE_RECREATION = 0 + + def __init__(self, tag=None, attrs={}, payload=[], parent=None, nsp=None, + node_built=False, node=None): + """ + Takes "tag" argument as the name of node (prepended by namespace, if + needed and separated from it by a space), attrs dictionary as the set of + arguments, payload list as the set of textual strings and child nodes + that this node carries within itself and "parent" argument that is + another node that this one will be the child of. Also the __init__ can be + provided with "node" argument that is either a text string containing + exactly one node or another Node instance to begin with. If both "node" + and other arguments is provided then the node initially created as + replica of "node" provided and then modified to be compliant with other + arguments. + """ if node: if self.FORCE_NODE_RECREATION and isinstance(node, Node): - node=str(node) + node = str(node) if not isinstance(node, Node): - node=NodeBuilder(node,self) + node = NodeBuilder(node,self) node_built = True else: - self.name,self.namespace,self.attrs,self.data,self.kids,self.parent,self.nsd = node.name,node.namespace,{},[],[],node.parent,{} - for key in node.attrs.keys(): self.attrs[key]=node.attrs[key] - for data in node.data: self.data.append(data) - for kid in node.kids: self.kids.append(kid) - for k,v in node.nsd.items(): self.nsd[k] = v - else: self.name,self.namespace,self.attrs,self.data,self.kids,self.parent,self.nsd = 'tag','',{},[],[],None,{} + self.name, self.namespace, self.attrs, self.data, self.kids, self.parent, self.nsd = node.name, node.namespace, {}, [], [], node.parent, {} + for key in node.attrs.keys(): + self.attrs[key] = node.attrs[key] + for data in node.data: + self.data.append(data) + for kid in node.kids: + self.kids.append(kid) + for k,v in node.nsd.items(): + self.nsd[k] = v + else: + self.name, self.namespace, self.attrs, self.data, self.kids, self.parent, self.nsd = 'tag', '', {}, [], [], None, {} if parent: self.parent = parent self.nsp_cache = {} @@ -94,10 +126,12 @@ class Node(object): self.name = tag if isinstance(payload, basestring): payload=[payload] for i in payload: - if isinstance(i, Node): self.addChild(node=i) - else: self.data.append(ustr(i)) + if isinstance(i, Node): + self.addChild(node=i) + else: + self.data.append(ustr(i)) - def lookup_nsp(self,pfx=''): + def lookup_nsp(self, pfx=''): ns = self.nsd.get(pfx,None) if ns is None: ns = self.nsp_cache.get(pfx,None) @@ -109,9 +143,11 @@ class Node(object): return 'http://www.gajim.org/xmlns/undeclared' return ns - def __str__(self,fancy=0): - ''' Method used to dump node into textual representation. - if "fancy" argument is set to True produces indented output for readability.''' + def __str__(self, fancy=0): + """ + Method used to dump node into textual representation. If "fancy" argument + is set to True produces indented output for readability + """ s = (fancy-1) * 2 * ' ' + "<" + self.name if self.namespace: if not self.parent or self.parent.namespace!=self.namespace: @@ -142,9 +178,12 @@ class Node(object): s = s + "" if fancy: s = s + "\n" return s + def addChild(self, name=None, attrs={}, payload=[], namespace=None, node=None): - ''' If "node" argument is provided, adds it as child node. Else creates new node from - the other arguments' values and adds it as well.''' + """ + If "node" argument is provided, adds it as child node. Else creates new + node from the other arguments' values and adds it as well + """ if 'xmlns' in attrs: raise AttributeError("Use namespace=x instead of attrs={'xmlns':x}") if node: @@ -155,196 +194,338 @@ class Node(object): newnode.setNamespace(namespace) self.kids.append(newnode) return newnode + def addData(self, data): - ''' Adds some CDATA to node. ''' + """ + Add some CDATA to node + """ self.data.append(ustr(data)) + def clearData(self): - ''' Removes all CDATA from the node. ''' - self.data=[] + """ + Remove all CDATA from the node + """ + self.data = [] + def delAttr(self, key): - ''' Deletes an attribute "key" ''' + """ + Delete an attribute "key" + """ del self.attrs[key] + def delChild(self, node, attrs={}): - ''' Deletes the "node" from the node's childs list, if "node" is an instance. - Else deletes the first node that have specified name and (optionally) attributes. ''' - if not isinstance(node, Node): node=self.getTag(node,attrs) + """ + Delete the "node" from the node's childs list, if "node" is an instance. + Else delete the first node that have specified name and (optionally) + attributes + """ + if not isinstance(node, Node): + node = self.getTag(node,attrs) self.kids.remove(node) return node + def getAttrs(self): - ''' Returns all node's attributes as dictionary. ''' + """ + Return all node's attributes as dictionary + """ return self.attrs + def getAttr(self, key): - ''' Returns value of specified attribute. ''' + """ + Return value of specified attribute + """ return self.attrs.get(key) + def getChildren(self): - ''' Returns all node's child nodes as list. ''' + """ + Return all node's child nodes as list + """ return self.kids + def getData(self): - ''' Returns all node CDATA as string (concatenated). ''' + """ + Return all node CDATA as string (concatenated) + """ return ''.join(self.data) + def getName(self): - ''' Returns the name of node ''' + """ + Return the name of node + """ return self.name + def getNamespace(self): - ''' Returns the namespace of node ''' + """ + Return the namespace of node + """ return self.namespace + def getParent(self): - ''' Returns the parent of node (if present). ''' + """ + Returns the parent of node (if present) + """ return self.parent + def getPayload(self): - ''' Return the payload of node i.e. list of child nodes and CDATA entries. - F.e. for "text1 text2" will be returned list: - ['text1', , , ' text2']. ''' - ret=[] + """ + Return the payload of node i.e. list of child nodes and CDATA entries. + F.e. for "text1 text2" will be returned + list: ['text1', , , ' text2'] + """ + ret = [] for i in range(len(self.kids)+len(self.data)+1): try: - if self.data[i]: ret.append(self.data[i]) - except IndexError: pass - try: ret.append(self.kids[i]) - except IndexError: pass + if self.data[i]: + ret.append(self.data[i]) + except IndexError: + pass + try: + ret.append(self.kids[i]) + except IndexError: + pass return ret + def getTag(self, name, attrs={}, namespace=None): - ''' Filters all child nodes using specified arguments as filter. - Returns the first found or None if not found. ''' + """ + Filter all child nodes using specified arguments as filter. Return the + first found or None if not found + """ return self.getTags(name, attrs, namespace, one=1) - def getTagAttr(self,tag,attr): - ''' Returns attribute value of the child with specified name (or None if no such attribute).''' + + def getTagAttr(self, tag, attr): + """ + Return attribute value of the child with specified name (or None if no + such attribute) + """ try: return self.getTag(tag).attrs[attr] except: return None + def getTagData(self,tag): - ''' Returns cocatenated CDATA of the child with specified name.''' + """ + Return cocatenated CDATA of the child with specified name + """ try: return self.getTag(tag).getData() except Exception: return None + def getTags(self, name, attrs={}, namespace=None, one=0): - ''' Filters all child nodes using specified arguments as filter. - Returns the list of nodes found. ''' - nodes=[] + """ + Filter all child nodes using specified arguments as filter. Returns the + list of nodes found + """ + nodes = [] for node in self.kids: - if namespace and namespace!=node.getNamespace(): continue + if namespace and namespace != node.getNamespace(): + continue if node.getName() == name: for key in attrs.keys(): - if key not in node.attrs or node.attrs[key]!=attrs[key]: break - else: nodes.append(node) - if one and nodes: return nodes[0] - if not one: return nodes + if key not in node.attrs or node.attrs[key]!=attrs[key]: + break + else: + nodes.append(node) + if one and nodes: + return nodes[0] + if not one: + return nodes def iterTags(self, name, attrs={}, namespace=None): - ''' Iterate over all children using specified arguments as filter. ''' + """ + Iterate over all children using specified arguments as filter + """ for node in self.kids: - if namespace is not None and namespace!=node.getNamespace(): continue + if namespace is not None and namespace != node.getNamespace(): + continue if node.getName() == name: for key in attrs.keys(): if key not in node.attrs or \ - node.attrs[key]!=attrs[key]: break + node.attrs[key]!=attrs[key]: + break else: yield node def setAttr(self, key, val): - ''' Sets attribute "key" with the value "val". ''' - self.attrs[key]=val + """ + Set attribute "key" with the value "val" + """ + self.attrs[key] = val + def setData(self, data): - ''' Sets node's CDATA to provided string. Resets all previous CDATA!''' - self.data=[ustr(data)] - def setName(self,val): - ''' Changes the node name. ''' + """ + Set node's CDATA to provided string. Resets all previous CDATA! + """ + self.data = [ustr(data)] + + def setName(self, val): + """ + Change the node name + """ self.name = val + def setNamespace(self, namespace): - ''' Changes the node namespace. ''' - self.namespace=namespace + """ + Changes the node namespace + """ + self.namespace = namespace + def setParent(self, node): - ''' Sets node's parent to "node". WARNING: do not checks if the parent already present - and not removes the node from the list of childs of previous parent. ''' + """ + Set node's parent to "node". WARNING: do not checks if the parent already + present and not removes the node from the list of childs of previous + parent + """ self.parent = node - def setPayload(self,payload,add=0): - ''' Sets node payload according to the list specified. WARNING: completely replaces all node's - previous content. If you wish just to add child or CDATA - use addData or addChild methods. ''' - if isinstance(payload, basestring): payload=[payload] - if add: self.kids+=payload - else: self.kids=payload + + def setPayload(self, payload, add=0): + """ + Set node payload according to the list specified. WARNING: completely + replaces all node's previous content. If you wish just to add child or + CDATA - use addData or addChild methods + """ + if isinstance(payload, basestring): + payload = [payload] + if add: + self.kids += payload + else: + self.kids = payload + def setTag(self, name, attrs={}, namespace=None): - ''' Same as getTag but if the node with specified namespace/attributes not found, creates such - node and returns it. ''' - node=self.getTags(name, attrs, namespace=namespace, one=1) - if node: return node - else: return self.addChild(name, attrs, namespace=namespace) - def setTagAttr(self,tag,attr,val): - ''' Creates new node (if not already present) with name "tag" - and sets it's attribute "attr" to value "val". ''' + """ + Same as getTag but if the node with specified namespace/attributes not + found, creates such node and returns it + """ + node = self.getTags(name, attrs, namespace=namespace, one=1) + if node: + return node + else: + return self.addChild(name, attrs, namespace=namespace) + + def setTagAttr(self, tag, attr, val): + """ + Create new node (if not already present) with name "tag" and set it's + attribute "attr" to value "val" + """ try: - self.getTag(tag).attrs[attr]=val + self.getTag(tag).attrs[attr] = val except Exception: - self.addChild(tag,attrs={attr:val}) - def setTagData(self,tag,val,attrs={}): - ''' Creates new node (if not already present) with name "tag" and (optionally) attributes "attrs" - and sets it's CDATA to string "val". ''' + self.addChild(tag, attrs={attr: val}) + + def setTagData(self, tag, val, attrs={}): + """ + Creates new node (if not already present) with name "tag" and + (optionally) attributes "attrs" and sets it's CDATA to string "val" + """ try: self.getTag(tag,attrs).setData(ustr(val)) except Exception: - self.addChild(tag,attrs,payload=[ustr(val)]) - def has_attr(self,key): - ''' Checks if node have attribute "key".''' + self.addChild(tag,attrs,payload = [ustr(val)]) + + def has_attr(self, key): + """ + Check if node have attribute "key" + """ return key in self.attrs - def __getitem__(self,item): - ''' Returns node's attribute "item" value. ''' + + def __getitem__(self, item): + """ + Return node's attribute "item" value + """ return self.getAttr(item) - def __setitem__(self,item,val): - ''' Sets node's attribute "item" value. ''' - return self.setAttr(item,val) - def __delitem__(self,item): - ''' Deletes node's attribute "item". ''' + + def __setitem__(self, item, val): + """ + Set node's attribute "item" value + """ + return self.setAttr(item, val) + + def __delitem__(self, item): + """ + Delete node's attribute "item" + """ return self.delAttr(item) - def __contains__(self,item): - """ Checks if node has attribute "item" """ + + def __contains__(self, item): + """ + Check if node has attribute "item" + """ return self.has_attr(item) - def __getattr__(self,attr): - ''' Reduce memory usage caused by T/NT classes - use memory only when needed. ''' - if attr=='T': - self.T=T(self) + + def __getattr__(self, attr): + """ + Reduce memory usage caused by T/NT classes - use memory only when needed + """ + if attr == 'T': + self.T = T(self) return self.T - if attr=='NT': - self.NT=NT(self) + if attr == 'NT': + self.NT = NT(self) return self.NT raise AttributeError class T: - ''' Auxiliary class used to quick access to node's child nodes. ''' - def __init__(self,node): self.__dict__['node']=node - def __getattr__(self,attr): return self.node.setTag(attr) - def __setattr__(self,attr,val): - if isinstance(val,Node): Node.__init__(self.node.setTag(attr),node=val) - else: return self.node.setTagData(attr,val) - def __delattr__(self,attr): return self.node.delChild(attr) + """ + Auxiliary class used to quick access to node's child nodes + """ + + def __init__(self, node): + self.__dict__['node'] = node + + def __getattr__(self, attr): + return self.node.setTag(attr) + + def __setattr__(self, attr, val): + if isinstance(val,Node): + Node.__init__(self.node.setTag(attr), node=val) + else: + return self.node.setTagData(attr, val) + + def __delattr__(self, attr): + return self.node.delChild(attr) class NT(T): - ''' Auxiliary class used to quick create node's child nodes. ''' - def __getattr__(self,attr): return self.node.addChild(attr) - def __setattr__(self,attr,val): - if isinstance(val,Node): self.node.addChild(attr,node=val) - else: return self.node.addChild(attr,payload=[val]) + """ + Auxiliary class used to quick create node's child nodes + """ + + def __getattr__(self, attr): + return self.node.addChild(attr) + + def __setattr__(self, attr, val): + if isinstance(val,Node): + self.node.addChild(attr,node=val) + else: + return self.node.addChild(attr, payload=[val]) class NodeBuilder: - ''' Builds a Node class minidom from data parsed to it. This class used for two purposes: - 1. Creation an XML Node from a textual representation. F.e. reading a config file. See an XML2Node method. - 2. Handling an incoming XML stream. This is done by mangling - the __dispatch_depth parameter and redefining the dispatch method. - You do not need to use this class directly if you do not designing your own XML handler.''' - def __init__(self,data=None,initial_node=None): - ''' Takes two optional parameters: "data" and "initial_node". - By default class initialised with empty Node class instance. - Though, if "initial_node" is provided it used as "starting point". - You can think about it as of "node upgrade". - "data" (if provided) feeded to parser immidiatedly after instance init. - ''' + """ + Builds a Node class minidom from data parsed to it. This class used for two + purposes: + 1. Creation an XML Node from a textual representation. F.e. reading a + config file. See an XML2Node method. + 2. Handling an incoming XML stream. This is done by mangling the + __dispatch_depth parameter and redefining the dispatch method. + + You do not need to use this class directly if you do not designing your own + XML handler + """ + + def __init__(self, data=None, initial_node=None): + """ + Take two optional parameters: "data" and "initial_node" + + By default class initialised with empty Node class instance. Though, if + "initial_node" is provided it used as "starting point". You can think + about it as of "node upgrade". "data" (if provided) feeded to parser + immidiatedly after instance init. + """ log.debug("Preparing to handle incoming XML stream.") self._parser = xml.parsers.expat.ParserCreate() - self._parser.StartElementHandler = self.starttag - self._parser.EndElementHandler = self.endtag + self._parser.StartElementHandler = self.starttag + self._parser.EndElementHandler = self.endtag self._parser.StartNamespaceDeclHandler = self.handle_namespace_start - self._parser.CharacterDataHandler = self.handle_cdata + self._parser.CharacterDataHandler = self.handle_cdata self._parser.buffer_text = True self.Parse = self._parser.Parse @@ -369,15 +550,19 @@ class NodeBuilder: self.data_buffer = None def destroy(self): - ''' Method used to allow class instance to be garbage-collected. ''' + """ + Method used to allow class instance to be garbage-collected + """ self.check_data_buffer() - self._parser.StartElementHandler = None - self._parser.EndElementHandler = None - self._parser.CharacterDataHandler = None + self._parser.StartElementHandler = None + self._parser.EndElementHandler = None + self._parser.CharacterDataHandler = None self._parser.StartNamespaceDeclHandler = None def starttag(self, tag, attrs): - '''XML Parser callback. Used internally''' + """ + XML Parser callback. Used internally + """ self.check_data_buffer() self._inc_depth() log.info("STARTTAG.. DEPTH -> %i , tag -> %s, attrs -> %s" % (self.__depth, tag, `attrs`)) @@ -410,8 +595,11 @@ class NodeBuilder: if not self.last_is_data and self._ptr.parent: self._ptr.parent.data.append('') self.last_is_data = 0 + def endtag(self, tag ): - '''XML Parser callback. Used internally''' + """ + XML Parser callback. Used internally + """ log.info("DEPTH -> %i , tag -> %s" % (self.__depth, tag)) self.check_data_buffer() if self.__depth == self._dispatch_depth: @@ -439,25 +627,42 @@ class NodeBuilder: self.last_is_data = 1 def handle_namespace_start(self, prefix, uri): - '''XML Parser callback. Used internally''' + """ + XML Parser callback. Used internally + """ self.check_data_buffer() def getDom(self): - ''' Returns just built Node. ''' + """ + Return just built Node + """ self.check_data_buffer() return self._mini_dom - def dispatch(self,stanza): - ''' Gets called when the NodeBuilder reaches some level of depth on it's way up with the built - node as argument. Can be redefined to convert incoming XML stanzas to program events. ''' - def stream_header_received(self,ns,tag,attrs): - ''' Method called when stream just opened. ''' + + def dispatch(self, stanza): + """ + Get called when the NodeBuilder reaches some level of depth on it's way + up with the built node as argument. Can be redefined to convert incoming + XML stanzas to program events + """ + pass + + def stream_header_received(self, ns, tag, attrs): + """ + Method called when stream just opened + """ self.check_data_buffer() + def stream_footer_received(self): - ''' Method called when stream just closed. ''' + """ + Method called when stream just closed + """ self.check_data_buffer() def has_received_endtag(self, level=0): - ''' Return True if at least one end tag was seen (at level) ''' + """ + Return True if at least one end tag was seen (at level) + """ return self.__depth <= level and self.__max_depth > level def _inc_depth(self): @@ -470,14 +675,20 @@ class NodeBuilder: self.__depth -= 1 def XML2Node(xml): - ''' Converts supplied textual string into XML node. Handy f.e. for reading configuration file. - Raises xml.parser.expat.parsererror if provided string is not well-formed XML. ''' + """ + Convert supplied textual string into XML node. Handy f.e. for reading + configuration file. Raises xml.parser.expat.parsererror if provided string + is not well-formed XML + """ return NodeBuilder(xml).getDom() def BadXML2Node(xml): - ''' Converts supplied textual string into XML node. Survives if xml data is cutted half way round. - I.e. "some text
some more text". Will raise xml.parser.expat.parsererror on misplaced - tags though. F.e. "some text
some more text
" will not work.''' + """ + Convert supplied textual string into XML node. Survives if xml data is + cutted half way round. I.e. "some text
some more text". Will raise + xml.parser.expat.parsererror on misplaced tags though. F.e. "some text +
some more text
" will not work + """ return NodeBuilder(xml).getDom() # vim: se ts=3: