Philipp Hörist pushed to branch master at gajim / python-nbxmpp

Commits:

5 changed files:

Changes:

  • nbxmpp/__init__.py
    ... ... @@ -15,4 +15,4 @@ from .client_nb import NonBlockingClient
    15 15
     from .plugin import PlugIn
    
    16 16
     from .smacks import Smacks
    
    17 17
     
    
    18
    -__version__ = "0.9.90.1"
    18
    +__version__ = "0.9.90.2"

  • nbxmpp/dispatcher_nb.py
    ... ... @@ -27,10 +27,18 @@ import uuid
    27 27
     import logging
    
    28 28
     from xml.parsers.expat import ExpatError
    
    29 29
     
    
    30
    -from . import simplexml
    
    31
    -from .plugin import PlugIn
    
    32
    -from .protocol import (NS_STREAMS, NS_HTTP_BIND, Iq, Presence,
    
    33
    -        Message, Protocol, Node, Error, ERR_FEATURE_NOT_IMPLEMENTED)
    
    30
    +from nbxmpp.simplexml import NodeBuilder
    
    31
    +from nbxmpp.plugin import PlugIn
    
    32
    +from nbxmpp.protocol import NS_STREAMS
    
    33
    +from nbxmpp.protocol import NS_HTTP_BIND
    
    34
    +from nbxmpp.protocol import NodeProcessed
    
    35
    +from nbxmpp.protocol import Iq
    
    36
    +from nbxmpp.protocol import Presence
    
    37
    +from nbxmpp.protocol import Message
    
    38
    +from nbxmpp.protocol import Protocol
    
    39
    +from nbxmpp.protocol import Node
    
    40
    +from nbxmpp.protocol import Error
    
    41
    +from nbxmpp.protocol import ERR_FEATURE_NOT_IMPLEMENTED
    
    34 42
     
    
    35 43
     
    
    36 44
     log = logging.getLogger('nbxmpp.dispatcher_nb')
    
    ... ... @@ -177,7 +185,7 @@ class XMPPDispatcher(PlugIn):
    177 185
             Send an initial stream header
    
    178 186
             """
    
    179 187
             self._owner.Connection.sendqueue = []
    
    180
    -        self.Stream = simplexml.NodeBuilder()
    
    188
    +        self.Stream = NodeBuilder()
    
    181 189
             self.Stream.dispatch = self.dispatch
    
    182 190
             self.Stream._dispatch_depth = 2
    
    183 191
             self.Stream.stream_header_received = self._check_stream_start
    
    ... ... @@ -258,7 +266,7 @@ class XMPPDispatcher(PlugIn):
    258 266
             self.RegisterProtocol('unknown', Protocol, xmlns=xmlns)
    
    259 267
             self.RegisterProtocol('default', Protocol, xmlns=xmlns)
    
    260 268
     
    
    261
    -    def RegisterProtocol(self, tag_name, Proto, xmlns=None, order='info'):
    
    269
    +    def RegisterProtocol(self, tag_name, proto, xmlns=None, order='info'):
    
    262 270
             """
    
    263 271
             Used to declare some top-level stanza name to dispatcher
    
    264 272
     
    
    ... ... @@ -266,20 +274,18 @@ class XMPPDispatcher(PlugIn):
    266 274
             presence protocols are registered by default.
    
    267 275
             """
    
    268 276
             if not xmlns:
    
    269
    -            xmlns=self._owner.defaultNamespace
    
    270
    -        log.debug('Registering protocol "%s" as %s(%s)' %(tag_name, Proto, xmlns))
    
    271
    -        self.handlers[xmlns][tag_name] = {type:Proto, 'default':[]}
    
    277
    +            xmlns = self._owner.defaultNamespace
    
    278
    +        log.debug('Registering protocol "%s" as %s(%s)', tag_name, proto, xmlns)
    
    279
    +        self.handlers[xmlns][tag_name] = {'type': proto, 'default': []}
    
    272 280
     
    
    273
    -    def RegisterNamespaceHandler(self, xmlns, handler, typ='', ns='',
    
    274
    -                    makefirst=0, system=0):
    
    281
    +    def RegisterNamespaceHandler(self, xmlns, handler, typ='', ns='', system=0):
    
    275 282
             """
    
    276 283
             Register handler for processing all stanzas for specified namespace
    
    277 284
             """
    
    278
    -        self.RegisterHandler('default', handler, typ, ns, xmlns, makefirst,
    
    279
    -                system)
    
    285
    +        self.RegisterHandler('default', handler, typ, ns, xmlns, system)
    
    280 286
     
    
    281 287
         def RegisterHandler(self, name, handler, typ='', ns='', xmlns=None,
    
    282
    -                    makefirst=False, system=False):
    
    288
    +                        system=False, priority=50):
    
    283 289
             """
    
    284 290
             Register user callback as stanzas handler of declared type
    
    285 291
     
    
    ... ... @@ -295,40 +301,44 @@ class XMPPDispatcher(PlugIn):
    295 301
                     value will match
    
    296 302
             :param ns: namespace of child that stanza must contain.
    
    297 303
             :param xmlns: xml namespace
    
    298
    -        :param makefirst: insert handler in the beginning of handlers list instead
    
    299
    -                of      adding it to the end. Note that more common handlers i.e. w/o "typ"
    
    300
    -                and " will be called first nevertheless.
    
    301 304
             :param system: call handler even if NodeProcessed Exception were raised
    
    302
    -                already.
    
    305
    +                       already.
    
    306
    +        :param priority: The priority of the handler, higher get called later
    
    303 307
             """
    
    304 308
             if not xmlns:
    
    305
    -            xmlns=self._owner.defaultNamespace
    
    306
    -        log.debug('Registering handler %s for "%s" type->%s ns->%s(%s)' %
    
    307
    -                (handler, name, typ, ns, xmlns))
    
    309
    +            xmlns = self._owner.defaultNamespace
    
    310
    +
    
    308 311
             if not typ and not ns:
    
    309
    -            typ='default'
    
    312
    +            typ = 'default'
    
    313
    +
    
    314
    +        log.debug(
    
    315
    +            'Registering handler %s for "%s" type->%s ns->%s(%s) priority->%s',
    
    316
    +            handler, name, typ, ns, xmlns, priority)
    
    317
    +
    
    310 318
             if xmlns not in self.handlers:
    
    311 319
                 self.RegisterNamespace(xmlns, 'warn')
    
    312 320
             if name not in self.handlers[xmlns]:
    
    313 321
                 self.RegisterProtocol(name, Protocol, xmlns, 'warn')
    
    314
    -        if typ+ns not in self.handlers[xmlns][name]:
    
    315
    -            self.handlers[xmlns][name][typ+ns]=[]
    
    316
    -        if makefirst:
    
    317
    -            self.handlers[xmlns][name][typ+ns].insert(0, {'func':handler,
    
    318
    -                    'system':system})
    
    319
    -        else:
    
    320
    -            self.handlers[xmlns][name][typ+ns].append({'func':handler,
    
    321
    -                    'system':system})
    
    322
    +
    
    323
    +        specific = typ + ns
    
    324
    +        if specific not in self.handlers[xmlns][name]:
    
    325
    +            self.handlers[xmlns][name][specific] = []
    
    326
    +
    
    327
    +        self.handlers[xmlns][name][specific].append(
    
    328
    +            {'func': handler,
    
    329
    +             'system': system,
    
    330
    +             'priority': priority,
    
    331
    +             'specific': specific})
    
    322 332
     
    
    323 333
         def RegisterHandlerOnce(self, name, handler, typ='', ns='', xmlns=None,
    
    324
    -                    makefirst=0, system=0):
    
    334
    +                            system=0):
    
    325 335
             """
    
    326 336
             Unregister handler after first call (not implemented yet)
    
    327 337
             """
    
    328 338
             # FIXME Drop or implement
    
    329 339
             if not xmlns:
    
    330 340
                 xmlns = self._owner.defaultNamespace
    
    331
    -        self.RegisterHandler(name, handler, typ, ns, xmlns, makefirst, system)
    
    341
    +        self.RegisterHandler(name, handler, typ, ns, xmlns, system)
    
    332 342
     
    
    333 343
         def UnregisterHandler(self, name, handler, typ='', ns='', xmlns=None):
    
    334 344
             """
    
    ... ... @@ -338,18 +348,26 @@ class XMPPDispatcher(PlugIn):
    338 348
             if not xmlns:
    
    339 349
                 xmlns = self._owner.defaultNamespace
    
    340 350
             if not typ and not ns:
    
    341
    -            typ='default'
    
    351
    +            typ = 'default'
    
    342 352
             if xmlns not in self.handlers:
    
    343 353
                 return
    
    344 354
             if name not in self.handlers[xmlns]:
    
    345 355
                 return
    
    346
    -        if typ+ns not in self.handlers[xmlns][name]:
    
    356
    +
    
    357
    +        specific = typ + ns
    
    358
    +        if specific not in self.handlers[xmlns][name]:
    
    347 359
                 return
    
    348
    -        for pack in self.handlers[xmlns][name][typ+ns]:
    
    349
    -            if pack['func'] == handler:
    
    360
    +        for handler_dict in self.handlers[xmlns][name][specific]:
    
    361
    +            if handler_dict['func'] == handler:
    
    350 362
                     try:
    
    351
    -                    self.handlers[xmlns][name][typ+ns].remove(pack)
    
    363
    +                    self.handlers[xmlns][name][specific].remove(handler_dict)
    
    364
    +                    log.debug(
    
    365
    +                        'Unregister handler %s for "%s" type->%s ns->%s(%s)',
    
    366
    +                        handler, name, typ, ns, xmlns)
    
    352 367
                     except ValueError:
    
    368
    +                    log.warning(
    
    369
    +                        'Unregister failed: %s for "%s" type->%s ns->%s(%s)',
    
    370
    +                        handler, name, typ, ns, xmlns)
    
    353 371
                         pass
    
    354 372
     
    
    355 373
         def RegisterDefaultHandler(self, handler):
    
    ... ... @@ -401,16 +419,11 @@ class XMPPDispatcher(PlugIn):
    401 419
             else:
    
    402 420
                 log.warning('Received unhandled event: %s' % event)
    
    403 421
     
    
    404
    -    def dispatch(self, stanza, session=None, direct=0):
    
    422
    +    def dispatch(self, stanza, session=None):
    
    405 423
             """
    
    406 424
             Main procedure that performs XMPP stanza recognition and calling
    
    407 425
             apppropriate handlers for it. Called by simplexml
    
    408 426
             """
    
    409
    -        # FIXME: Where do we set session and direct. Why? What are those intended
    
    410
    -        # to do?
    
    411
    -
    
    412
    -        #log.info('dispatch called: stanza = %s, session = %s, direct= %s'
    
    413
    -        #       % (stanza, session, direct))
    
    414 427
     
    
    415 428
             self.Event('', 'STANZA RECEIVED', stanza)
    
    416 429
     
    
    ... ... @@ -428,8 +441,6 @@ class XMPPDispatcher(PlugIn):
    428 441
     
    
    429 442
             xmlns = stanza.getNamespace()
    
    430 443
     
    
    431
    -        # log.info('in dispatch, getting ns for %s, and the ns is %s'
    
    432
    -        # % (stanza, xmlns))
    
    433 444
             if xmlns not in self.handlers:
    
    434 445
                 log.warning('Unknown namespace: %s', xmlns)
    
    435 446
                 xmlns = 'unknown'
    
    ... ... @@ -438,14 +449,13 @@ class XMPPDispatcher(PlugIn):
    438 449
                 if name not in ('features', 'stream'):
    
    439 450
                     log.warning('Unknown stanza: %s', stanza)
    
    440 451
                 else:
    
    441
    -                log.debug('Got %s/%s stanza' % (xmlns, name))
    
    452
    +                log.debug('Got %s / %s stanza' % (xmlns, name))
    
    442 453
                 name = 'unknown'
    
    443 454
             else:
    
    444
    -            log.debug('Got %s/%s stanza' % (xmlns, name))
    
    455
    +            log.debug('Got %s / %s stanza' % (xmlns, name))
    
    445 456
     
    
    446
    -        if stanza.__class__.__name__ == 'Node':
    
    447
    -            # FIXME: this cannot work
    
    448
    -            stanza = self.handlers[xmlns][name][type](node=stanza)
    
    457
    +        # Convert simplexml to Protocol object
    
    458
    +        stanza = self.handlers[xmlns][name]['type'](node=stanza)
    
    449 459
     
    
    450 460
             typ = stanza.getType()
    
    451 461
             if name == 'message' and not typ:
    
    ... ... @@ -461,48 +471,55 @@ class XMPPDispatcher(PlugIn):
    461 471
             if hasattr(self._owner, 'Smacks'):
    
    462 472
                 self._owner.Smacks.count_incoming(stanza.getName())
    
    463 473
     
    
    464
    -        list_ = ['default'] # we will use all handlers:
    
    465
    -        if typ in self.handlers[xmlns][name]:
    
    466
    -            list_.append(typ) # from very common...
    
    467
    -        for prop in stanza.props:
    
    468
    -            if prop in self.handlers[xmlns][name]:
    
    469
    -                list_.append(prop)
    
    470
    -            if typ and typ+prop in self.handlers[xmlns][name]:
    
    471
    -                list_.append(typ+prop)  # ...to very particular
    
    472
    -
    
    473
    -        chain = self.handlers[xmlns]['default']['default']
    
    474
    -        for key in list_:
    
    475
    -            if key:
    
    476
    -                chain = chain + self.handlers[xmlns][name][key]
    
    477
    -        log.debug('Chain: %s', [key for key in list_ if key])
    
    478
    -
    
    474
    +        processed = False
    
    479 475
             if ID in session._expected:
    
    480
    -            user = 0
    
    481 476
                 if isinstance(session._expected[ID], tuple):
    
    482 477
                     cb, args = session._expected[ID]
    
    483
    -                log.debug("Expected stanza arrived. Callback %s(%s) found!" %
    
    484
    -                        (cb, args))
    
    478
    +                log.debug('Expected stanza arrived. Callback %s(%s) found',
    
    479
    +                          cb, args)
    
    485 480
                     try:
    
    486 481
                         cb(session,stanza,**args)
    
    487
    -                except Exception as typ:
    
    488
    -                    if typ.__class__.__name__ != 'NodeProcessed':
    
    489
    -                        raise
    
    482
    +                except NodeProcessed:
    
    483
    +                    pass
    
    484
    +                except Exception:
    
    485
    +                    raise
    
    490 486
                 else:
    
    491
    -                log.debug("Expected stanza arrived!")
    
    487
    +                log.debug('Expected stanza arrived')
    
    492 488
                     session._expected[ID] = stanza
    
    493
    -        else:
    
    494
    -            user = 1
    
    489
    +            processed = True
    
    490
    +
    
    491
    +        # Gather specifics depending on stanza properties
    
    492
    +        specifics = ['default']
    
    493
    +        if typ and typ in self.handlers[xmlns][name]:
    
    494
    +            specifics.append(typ)
    
    495
    +        for prop in stanza.props:
    
    496
    +            if prop in self.handlers[xmlns][name]:
    
    497
    +                specifics.append(prop)
    
    498
    +            if typ and typ + prop in self.handlers[xmlns][name]:
    
    499
    +                specifics.append(typ + prop)
    
    500
    +
    
    501
    +        # Create the handler chain
    
    502
    +        chain = []
    
    503
    +        chain += self.handlers[xmlns]['default']['default']
    
    504
    +        for specific in specifics:
    
    505
    +            chain += self.handlers[xmlns][name][specific]
    
    506
    +
    
    507
    +        # Sort chain with priority
    
    508
    +        chain.sort(key=lambda x: x['priority'])
    
    509
    +
    
    495 510
             for handler in chain:
    
    496
    -            if user or handler['system']:
    
    511
    +            if not processed or handler['system']:
    
    497 512
                     try:
    
    498
    -                    log.debug('Call handler: %s', handler['func'])
    
    513
    +                    log.info('Call handler: %s', handler['func'].__qualname__)
    
    499 514
                         handler['func'](session, stanza)
    
    500
    -                except Exception as typ:
    
    501
    -                    if typ.__class__.__name__ != 'NodeProcessed':
    
    502
    -                        self._pendingExceptions.insert(0, sys.exc_info())
    
    503
    -                        return
    
    504
    -                    user=0
    
    505
    -        if user and self._defaultHandler:
    
    515
    +                except NodeProcessed:
    
    516
    +                    processed = True
    
    517
    +                except Exception:
    
    518
    +                    self._pendingExceptions.insert(0, sys.exc_info())
    
    519
    +                    return
    
    520
    +
    
    521
    +        # Stanza was not processed call default handler
    
    522
    +        if not processed and self._defaultHandler:
    
    506 523
                 self._defaultHandler(session, stanza)
    
    507 524
     
    
    508 525
         def _WaitForData(self, data):
    
    ... ... @@ -592,7 +609,7 @@ class BOSHDispatcher(XMPPDispatcher):
    592 609
             """
    
    593 610
             Send an initial stream header
    
    594 611
             """
    
    595
    -        self.Stream = simplexml.NodeBuilder()
    
    612
    +        self.Stream = NodeBuilder()
    
    596 613
             self.Stream.dispatch = self.dispatch
    
    597 614
             self.Stream._dispatch_depth = 2
    
    598 615
             self.Stream.stream_header_received = self._check_stream_start
    
    ... ... @@ -625,7 +642,7 @@ class BOSHDispatcher(XMPPDispatcher):
    625 642
                 self.restart = False
    
    626 643
             return XMPPDispatcher.ProcessNonBlocking(self, data)
    
    627 644
     
    
    628
    -    def dispatch(self, stanza, session=None, direct=0):
    
    645
    +    def dispatch(self, stanza, session=None):
    
    629 646
             if stanza.getName() == 'body' and stanza.getNamespace() == NS_HTTP_BIND:
    
    630 647
     
    
    631 648
                 stanza_attrs = stanza.getAttrs()
    
    ... ... @@ -643,6 +660,6 @@ class BOSHDispatcher(XMPPDispatcher):
    643 660
                         # rewrite it to jabber:client
    
    644 661
                         if child.getNamespace() == NS_HTTP_BIND:
    
    645 662
                             child.setNamespace(self._owner.defaultNamespace)
    
    646
    -                    XMPPDispatcher.dispatch(self, child, session, direct)
    
    663
    +                    XMPPDispatcher.dispatch(self, child, session)
    
    647 664
             else:
    
    648
    -            XMPPDispatcher.dispatch(self, stanza, session, direct)
    665
    +            XMPPDispatcher.dispatch(self, stanza, session)

  • nbxmpp/protocol.py
    ... ... @@ -118,6 +118,7 @@ NS_MUC_ADMIN = NS_MUC + '#admin'
    118 118
     NS_MUC_OWNER      = NS_MUC + '#owner'
    
    119 119
     NS_MUC_UNIQUE     = NS_MUC + '#unique'
    
    120 120
     NS_MUC_CONFIG     = NS_MUC + '#roomconfig'
    
    121
    +NS_MUC_REQUEST    = NS_MUC + '#request'
    
    121 122
     NS_NICK           = 'http://jabber.org/protocol/nick'                 # XEP-0172
    
    122 123
     NS_OFFLINE        = 'http://www.jabber.org/jeps/jep-0030.html'        # XEP-0013
    
    123 124
     NS_OMEMO          = 'urn:xmpp:omemo:0'                                # XEP-0384
    

  • nbxmpp/roster_nb.py
    ... ... @@ -331,7 +331,7 @@ class NonBlockingRoster(PlugIn):
    331 331
             Also request roster from server if the 'request' argument is set. Used
    
    332 332
             internally
    
    333 333
             """
    
    334
    -        self._owner.RegisterHandler('iq', self.RosterIqHandler, 'result', NS_ROSTER, makefirst = 1)
    
    334
    +        self._owner.RegisterHandler('iq', self.RosterIqHandler, 'result', NS_ROSTER)
    
    335 335
             self._owner.RegisterHandler('iq', self.RosterIqHandler, 'set', NS_ROSTER)
    
    336 336
             self._owner.RegisterHandler('presence', self.PresenceHandler)
    
    337 337
             if request:
    

  • setup.py
    ... ... @@ -3,7 +3,7 @@
    3 3
     from setuptools import setup
    
    4 4
     
    
    5 5
     setup(name='nbxmpp',
    
    6
    -      version='0.9.90.1',
    
    6
    +      version='0.9.90.2',
    
    7 7
           description='Non blocking Jabber/XMPP module',
    
    8 8
           author='Yann Leboulanger',
    
    9 9
           author_email='asterix@lagaule.org',