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

Commits:

2 changed files:

Changes:

  • nbxmpp/modules/muclumbus.py
    ... ... @@ -22,15 +22,16 @@ from gi.repository import Soup
    22 22
     from nbxmpp.namespaces import Namespace
    
    23 23
     from nbxmpp.protocol import Node
    
    24 24
     from nbxmpp.protocol import Iq
    
    25
    -from nbxmpp.protocol import isResultNode
    
    26 25
     from nbxmpp.structs import MuclumbusItem
    
    27 26
     from nbxmpp.structs import MuclumbusResult
    
    28 27
     from nbxmpp.const import AnonymityMode
    
    29 28
     from nbxmpp.modules.dataforms import extend_form
    
    30
    -from nbxmpp.util import call_on_response
    
    31
    -from nbxmpp.util import callback
    
    32
    -from nbxmpp.util import raise_error
    
    29
    +from nbxmpp.errors import StanzaError
    
    30
    +from nbxmpp.errors import MalformedStanzaError
    
    31
    +from nbxmpp.task import iq_request_task
    
    32
    +from nbxmpp.task import http_request_task
    
    33 33
     from nbxmpp.modules.base import BaseModule
    
    34
    +from nbxmpp.modules.util import finalize
    
    34 35
     
    
    35 36
     
    
    36 37
     # API Documentation
    
    ... ... @@ -52,84 +53,58 @@ class Muclumbus(BaseModule):
    52 53
             self._proxy_resolver = proxy.get_resolver()
    
    53 54
             self._soup_session.props.proxy_resolver = self._proxy_resolver
    
    54 55
     
    
    55
    -    @call_on_response('_parameters_received')
    
    56
    +    @iq_request_task
    
    56 57
         def request_parameters(self, jid):
    
    57
    -        query = Iq(to=jid, typ='get')
    
    58
    -        query.addChild(node=Node('search',
    
    59
    -                                 attrs={'xmlns': Namespace.MUCLUMBUS}))
    
    60
    -        return query
    
    58
    +        task = yield
    
    61 59
     
    
    62
    -    @callback
    
    63
    -    def _parameters_received(self, stanza):
    
    64
    -        if not isResultNode(stanza):
    
    65
    -            return raise_error(self._log.info, stanza)
    
    60
    +        response = yield _make_parameter_request(jid)
    
    61
    +        if response.isError():
    
    62
    +            raise StanzaError(response)
    
    66 63
     
    
    67
    -        search = stanza.getTag('search', namespace=Namespace.MUCLUMBUS)
    
    64
    +        search = response.getTag('search', namespace=Namespace.MUCLUMBUS)
    
    68 65
             if search is None:
    
    69
    -            return raise_error(self._log.warning, stanza, 'stanza-malformed')
    
    66
    +            raise MalformedStanzaError('search node missing', response)
    
    70 67
     
    
    71 68
             dataform = search.getTag('x', namespace=Namespace.DATA)
    
    72 69
             if dataform is None:
    
    73
    -            return raise_error(self._log.warning, stanza, 'stanza-malformed')
    
    70
    +            raise MalformedStanzaError('dataform node missing', response)
    
    74 71
     
    
    75 72
             self._log.info('Muclumbus parameters received')
    
    76
    -        return extend_form(node=dataform)
    
    73
    +        yield finalize(task, extend_form(node=dataform))
    
    77 74
     
    
    78
    -    @call_on_response('_search_received')
    
    75
    +    @iq_request_task
    
    79 76
         def set_search(self, jid, dataform, items_per_page=50, after=None):
    
    80
    -        search = Node('search', attrs={'xmlns': Namespace.MUCLUMBUS})
    
    81
    -        search.addChild(node=dataform)
    
    82
    -        rsm = search.addChild('set', namespace=Namespace.RSM)
    
    83
    -        rsm.addChild('max').setData(items_per_page)
    
    84
    -        if after is not None:
    
    85
    -            rsm.addChild('after').setData(after)
    
    86
    -        query = Iq(to=jid, typ='get')
    
    87
    -        query.addChild(node=search)
    
    88
    -        return query
    
    89
    -
    
    90
    -    def set_http_search(self, uri, keywords, after=None,
    
    91
    -                        callback=None, user_data=None):
    
    92
    -        search = {'keywords': keywords}
    
    93
    -        if after is not None:
    
    94
    -            search['after'] = after
    
    95
    -
    
    96
    -        message = Soup.Message.new('POST', uri)
    
    97
    -        message.set_request('application/json',
    
    98
    -                            Soup.MemoryUse.COPY,
    
    99
    -                            json.dumps(search).encode())
    
    77
    +        _task = yield
    
    100 78
     
    
    101
    -        self._soup_session.queue_message(message,
    
    102
    -                                         self._http_search_received,
    
    103
    -                                         callback,
    
    104
    -                                         user_data)
    
    79
    +        response = yield _make_search_query(jid,
    
    80
    +                                            dataform,
    
    81
    +                                            items_per_page,
    
    82
    +                                            after)
    
    83
    +        if response.isError():
    
    84
    +            raise StanzaError(response)
    
    105 85
     
    
    106
    -    @callback
    
    107
    -    def _search_received(self, stanza):
    
    108
    -        if not isResultNode(stanza):
    
    109
    -            return raise_error(self._log.info, stanza)
    
    110
    -
    
    111
    -        result = stanza.getTag('result', namespace=Namespace.MUCLUMBUS)
    
    86
    +        result = response.getTag('result', namespace=Namespace.MUCLUMBUS)
    
    112 87
             if result is None:
    
    113
    -            return raise_error(self._log.warning, stanza, 'stanza-malformed')
    
    88
    +            raise MalformedStanzaError('result node missing', response)
    
    114 89
     
    
    115 90
             items = result.getTags('item')
    
    116 91
             if not items:
    
    117
    -            return MuclumbusResult(first=None,
    
    118
    -                                   last=None,
    
    119
    -                                   max=None,
    
    120
    -                                   end=True,
    
    121
    -                                   items=[])
    
    92
    +            yield MuclumbusResult(first=None,
    
    93
    +                                  last=None,
    
    94
    +                                  max=None,
    
    95
    +                                  end=True,
    
    96
    +                                  items=[])
    
    122 97
     
    
    123 98
             set_ = result.getTag('set', namespace=Namespace.RSM)
    
    124 99
             if set_ is None:
    
    125
    -            return raise_error(self._log.warning, stanza, 'stanza-malformed')
    
    100
    +            raise MalformedStanzaError('set node missing', response)
    
    126 101
     
    
    127 102
             first = set_.getTagData('first')
    
    128 103
             last = set_.getTagData('last')
    
    129 104
             try:
    
    130 105
                 max_ = int(set_.getTagData('max'))
    
    131 106
             except Exception:
    
    132
    -            return raise_error(self._log.warning, stanza, 'stanza-malformed')
    
    107
    +            raise MalformedStanzaError('invalid max value', response)
    
    133 108
     
    
    134 109
             results = []
    
    135 110
             for item in items:
    
    ... ... @@ -152,40 +127,47 @@ class Muclumbus(BaseModule):
    152 127
                                              language=language or '',
    
    153 128
                                              is_open=is_open,
    
    154 129
                                              anonymity_mode=anonymity_mode))
    
    155
    -        return MuclumbusResult(first=first,
    
    156
    -                               last=last,
    
    157
    -                               max=max_,
    
    158
    -                               end=len(items) < max_,
    
    159
    -                               items=results)
    
    130
    +        yield MuclumbusResult(first=first,
    
    131
    +                              last=last,
    
    132
    +                              max=max_,
    
    133
    +                              end=len(items) < max_,
    
    134
    +                              items=results)
    
    135
    +
    
    136
    +    @http_request_task
    
    137
    +    def set_http_search(self, uri, keywords, after=None):
    
    138
    +        _task = yield
    
    139
    +
    
    140
    +        search = {'keywords': keywords}
    
    141
    +        if after is not None:
    
    142
    +            search['after'] = after
    
    160 143
     
    
    161
    -    def _http_search_received(self, _session, message, callback, user_data):
    
    144
    +        message = Soup.Message.new('POST', uri)
    
    145
    +        message.set_request('application/json',
    
    146
    +                            Soup.MemoryUse.COPY,
    
    147
    +                            json.dumps(search).encode())
    
    162 148
     
    
    163
    -        def exec_callback(muclumbus_result):
    
    164
    -            if user_data is None:
    
    165
    -                callback(muclumbus_result)
    
    166
    -            else:
    
    167
    -                callback(muclumbus_result, user_data)
    
    149
    +        response_message = yield message
    
    168 150
     
    
    169
    -        soup_body = message.get_property('response-body')
    
    151
    +        soup_body = response_message.get_property('response-body')
    
    170 152
     
    
    171
    -        if message.status_code != 200:
    
    153
    +        if response_message.status_code != 200:
    
    172 154
                 self._log.warning(soup_body.data)
    
    173
    -            exec_callback(MuclumbusResult(first=None,
    
    174
    -                                          last=None,
    
    175
    -                                          max=None,
    
    176
    -                                          end=True,
    
    177
    -                                          items=[]))
    
    155
    +            yield MuclumbusResult(first=None,
    
    156
    +                                  last=None,
    
    157
    +                                  max=None,
    
    158
    +                                  end=True,
    
    159
    +                                  items=[])
    
    178 160
     
    
    179 161
             response = json.loads(soup_body.data)
    
    180 162
     
    
    181 163
             result = response['result']
    
    182 164
             items = result.get('items')
    
    183 165
             if items is None:
    
    184
    -            exec_callback(MuclumbusResult(first=None,
    
    185
    -                                          last=None,
    
    186
    -                                          max=None,
    
    187
    -                                          end=True,
    
    188
    -                                          items=[]))
    
    166
    +            yield MuclumbusResult(first=None,
    
    167
    +                                  last=None,
    
    168
    +                                  max=None,
    
    169
    +                                  end=True,
    
    170
    +                                  items=[])
    
    189 171
     
    
    190 172
             results = []
    
    191 173
             for item in items:
    
    ... ... @@ -203,8 +185,27 @@ class Muclumbus(BaseModule):
    203 185
                                   is_open=item['is_open'],
    
    204 186
                                   anonymity_mode=anonymity_mode))
    
    205 187
     
    
    206
    -        exec_callback(MuclumbusResult(first=None,
    
    207
    -                                      last=result['last'],
    
    208
    -                                      max=None,
    
    209
    -                                      end=not result['more'],
    
    210
    -                                      items=results))
    188
    +        yield MuclumbusResult(first=None,
    
    189
    +                              last=result['last'],
    
    190
    +                              max=None,
    
    191
    +                              end=not result['more'],
    
    192
    +                              items=results)
    
    193
    +
    
    194
    +
    
    195
    +def _make_parameter_request(jid):
    
    196
    +    query = Iq(to=jid, typ='get')
    
    197
    +    query.addChild(node=Node('search',
    
    198
    +                             attrs={'xmlns': Namespace.MUCLUMBUS}))
    
    199
    +    return query
    
    200
    +
    
    201
    +
    
    202
    +def _make_search_query(jid, dataform, items_per_page=50, after=None):
    
    203
    +    search = Node('search', attrs={'xmlns': Namespace.MUCLUMBUS})
    
    204
    +    search.addChild(node=dataform)
    
    205
    +    rsm = search.addChild('set', namespace=Namespace.RSM)
    
    206
    +    rsm.addChild('max').setData(items_per_page)
    
    207
    +    if after is not None:
    
    208
    +        rsm.addChild('after').setData(after)
    
    209
    +    query = Iq(to=jid, typ='get')
    
    210
    +    query.addChild(node=search)
    
    211
    +    return query

  • nbxmpp/task.py
    ... ... @@ -21,6 +21,8 @@ import logging
    21 21
     from enum import IntEnum
    
    22 22
     from functools import wraps
    
    23 23
     
    
    24
    +from gi.repository import Soup
    
    25
    +
    
    24 26
     from nbxmpp.errors import is_error
    
    25 27
     from nbxmpp.errors import CancelledError
    
    26 28
     from nbxmpp.simplexml import Node
    
    ... ... @@ -62,20 +64,33 @@ class TaskState(IntEnum):
    62 64
             return self == TaskState.CANCELLED
    
    63 65
     
    
    64 66
     
    
    67
    +def _setup_task(task, client, callback, user_data):
    
    68
    +    client.add_task(task)
    
    69
    +    task.set_finalize_func(client.remove_task)
    
    70
    +    task.set_user_data(user_data)
    
    71
    +    if callback is not None:
    
    72
    +        task.add_done_callback(callback)
    
    73
    +    task.start()
    
    74
    +    return task
    
    75
    +
    
    76
    +
    
    65 77
     def iq_request_task(func):
    
    66 78
         @wraps(func)
    
    67
    -    def func_wrapper(self, *args,
    
    68
    -                     callback=None, user_data=None, **kwargs):
    
    79
    +    def func_wrapper(self, *args, callback=None, user_data=None, **kwargs):
    
    69 80
             task = IqRequestTask(func(self, *args, **kwargs),
    
    70 81
                                  self._log,
    
    71 82
                                  self._client)
    
    72
    -        self._client.add_task(task)
    
    73
    -        task.set_finalize_func(self._client.remove_task)
    
    74
    -        task.set_user_data(user_data)
    
    75
    -        if callback is not None:
    
    76
    -            task.add_done_callback(callback)
    
    77
    -        task.start()
    
    78
    -        return task
    
    83
    +        return _setup_task(task, self._client, callback, user_data)
    
    84
    +    return func_wrapper
    
    85
    +
    
    86
    +
    
    87
    +def http_request_task(func):
    
    88
    +    @wraps(func)
    
    89
    +    def func_wrapper(self, *args, callback=None, user_data=None, **kwargs):
    
    90
    +        task = HTTPRequestTask(func(self, *args, **kwargs),
    
    91
    +                               self._log,
    
    92
    +                               self._soup_session)
    
    93
    +        return _setup_task(task, self._client, callback, user_data)
    
    79 94
         return func_wrapper
    
    80 95
     
    
    81 96
     
    
    ... ... @@ -287,3 +302,28 @@ class IqRequestTask(Task):
    287 302
         def _finalize(self):
    
    288 303
             self._client = None
    
    289 304
             super()._finalize()
    
    305
    +
    
    306
    +
    
    307
    +class HTTPRequestTask(Task):
    
    308
    +
    
    309
    +    '''
    
    310
    +    A Task for running HTTP requests
    
    311
    +
    
    312
    +    '''
    
    313
    +
    
    314
    +    _process_types = (Soup.Message,)
    
    315
    +
    
    316
    +    def __init__(self, gen, logger, session):
    
    317
    +        super().__init__(gen, logger)
    
    318
    +        self._session = session
    
    319
    +
    
    320
    +    def _run_async(self, message):
    
    321
    +        self._session.queue_message(message, self._async_finished, None)
    
    322
    +
    
    323
    +    def _async_finished(self, _session, message, _user_data):
    
    324
    +        if self._state != TaskState.CANCELLED:
    
    325
    +            self._next_step(message)
    
    326
    +
    
    327
    +    def _finalize(self):
    
    328
    +        self._session = None
    
    329
    +        super()._finalize()