comparison src/memory/disco.py @ 973:4a8903021fda

core (disco): findFeaturesSet and findServiceEntities don't use inlineCallbacks anymore, that allow a better error management (no more anonying debugger raise when discoInfo fails in debug mode)
author Goffi <goffi@goffi.org>
date Wed, 02 Apr 2014 12:31:23 +0200
parents 723f28cd15c7
children 301b342c697a
comparison
equal deleted inserted replaced
972:07b817f5a197 973:4a8903021fda
19 19
20 from sat.core.i18n import _ 20 from sat.core.i18n import _
21 from sat.core import exceptions 21 from sat.core import exceptions
22 from logging import debug, info, warning, error 22 from logging import debug, info, warning, error
23 from twisted.words.protocols.jabber import jid 23 from twisted.words.protocols.jabber import jid
24 from twisted.words.protocols.jabber.error import StanzaError
24 from twisted.internet import defer 25 from twisted.internet import defer
25 from sat.core.constants import Const as C 26 from sat.core.constants import Const as C
26 from wokkel import disco 27 from wokkel import disco
27 from base64 import b64encode 28 from base64 import b64encode
28 from hashlib import sha1 29 from hashlib import sha1
65 @param feature: feature namespace 66 @param feature: feature namespace
66 @param jid_: jid of the target, or None for profile's server 67 @param jid_: jid of the target, or None for profile's server
67 @param profile_key: %(doc_profile_key)s 68 @param profile_key: %(doc_profile_key)s
68 @return: a Deferred which fire a boolean (True if feature is available) 69 @return: a Deferred which fire a boolean (True if feature is available)
69 """ 70 """
70 disco_info = yield self.getInfos(jid_, profile_key) 71 disco_infos = yield self.getInfos(jid_, profile_key)
71 defer.returnValue(feature in disco_info.features) 72 defer.returnValue(feature in disco_infos.features)
72 73
73 @defer.inlineCallbacks 74 @defer.inlineCallbacks
74 def checkFeature(self, feature, jid_=None, profile_key=C.PROF_KEY_NONE): 75 def checkFeature(self, feature, jid_=None, profile_key=C.PROF_KEY_NONE):
75 """Like hasFeature, but raise an exception is feature is not Found 76 """Like hasFeature, but raise an exception is feature is not Found
76 77
79 @param profile_key: %(doc_profile_key)s 80 @param profile_key: %(doc_profile_key)s
80 @return: None if feature is found 81 @return: None if feature is found
81 82
82 @raise: exceptions.FeatureNotFound 83 @raise: exceptions.FeatureNotFound
83 """ 84 """
84 disco_info = yield self.getInfos(jid_, profile_key) 85 disco_infos = yield self.getInfos(jid_, profile_key)
85 if not feature in disco_info.features: 86 if not feature in disco_infos.features:
86 raise exceptions.FeatureNotFound 87 raise exceptions.FeatureNotFound
87 defer.returnValue(feature in disco_info) 88 defer.returnValue(feature in disco_infos)
88 89
89 @defer.inlineCallbacks
90 def getInfos(self, jid_=None, profile_key=C.PROF_KEY_NONE): 90 def getInfos(self, jid_=None, profile_key=C.PROF_KEY_NONE):
91 """get disco infos from jid_, filling capability hash if needed 91 """get disco infos from jid_, filling capability hash if needed
92 92
93 @param jid_: jid of the target, or None for profile's server 93 @param jid_: jid of the target, or None for profile's server
94 @param profile_key: %(doc_profile_key)s 94 @param profile_key: %(doc_profile_key)s
97 client = self.host.getClient(profile_key) 97 client = self.host.getClient(profile_key)
98 if jid_ is None: 98 if jid_ is None:
99 jid_ = jid.JID(client.jid.host) 99 jid_ = jid.JID(client.jid.host)
100 try: 100 try:
101 cap_hash = self.host.memory.getEntityData(jid_, [C.ENTITY_CAP_HASH], client.profile)[C.ENTITY_CAP_HASH] 101 cap_hash = self.host.memory.getEntityData(jid_, [C.ENTITY_CAP_HASH], client.profile)[C.ENTITY_CAP_HASH]
102 disco_info = self.hashes[cap_hash] 102 disco_infos = self.hashes[cap_hash]
103 return defer.succeed(disco_infos)
103 except KeyError: 104 except KeyError:
104 # capability hash is not available, we'll compute one 105 # capability hash is not available, we'll compute one
105 disco_info = yield client.disco.requestInfo(jid_) 106 def infosCb(disco_infos):
106 cap_hash = self.generateHash(disco_info) 107 cap_hash = self.generateHash(disco_infos)
107 self.hashes[cap_hash] = disco_info 108 self.hashes[cap_hash] = disco_infos
108 yield self.host.memory.updateEntityData(jid_, C.ENTITY_CAP_HASH, cap_hash, client.profile) 109 self.host.memory.updateEntityData(jid_, C.ENTITY_CAP_HASH, cap_hash, client.profile)
109 defer.returnValue(disco_info) 110 return disco_infos
111 d = client.disco.requestInfo(jid_)
112 d.addCallback(infosCb)
113 return d
110 114
111 @defer.inlineCallbacks 115 @defer.inlineCallbacks
112 def getItems(self, jid_=None, profile_key=C.PROF_KEY_NONE): 116 def getItems(self, jid_=None, profile_key=C.PROF_KEY_NONE):
113 """get disco items from jid_, cache them for our own server 117 """get disco items from jid_, cache them for our own server
114 118
131 items = yield client.disco.requestItems(jid_) 135 items = yield client.disco.requestItems(jid_)
132 136
133 defer.returnValue(items) 137 defer.returnValue(items)
134 138
135 139
136 @defer.inlineCallbacks 140 def _infosEb(self, failure, entity_jid):
141 failure.trap(StanzaError)
142 warning(_("Error while requesting [%(jid)s]: %(error)s") % {'jid': entity_jid.full(),
143 'error': failure.getErrorMessage()})
144
137 def findServiceEntities(self, category, type_, jid_=None, profile_key=C.PROF_KEY_NONE): 145 def findServiceEntities(self, category, type_, jid_=None, profile_key=C.PROF_KEY_NONE):
138 """Return all available items of an entity which correspond to (category, type_) 146 """Return all available items of an entity which correspond to (category, type_)
139 147
140 @param category: identity's category 148 @param category: identity's category
141 @param type_: identitiy's type 149 @param type_: identitiy's type
142 @param jid_: the jid of the target server (None for profile's server) 150 @param jid_: the jid of the target server (None for profile's server)
143 @param profile_key: %(doc_profile_key)s 151 @param profile_key: %(doc_profile_key)s
144 @return: a set of entities or None if no cached data were found 152 @return: a set of entities or None if no cached data were found
145 """ 153 """
146 found_identities = set() 154 found_entities = set()
147 items = yield self.getItems(jid_, profile_key) 155
148 for item in items: 156 def infosCb(infos, entity_jid):
149 infos = yield self.getInfos(item.entity, profile_key)
150 if (category, type_) in infos.identities: 157 if (category, type_) in infos.identities:
151 found_identities.add(item.entity) 158 found_entities.add(entity_jid)
152 159
153 defer.returnValue(found_identities) 160 def gotItems(items):
154 161 defers_list = []
155 @defer.inlineCallbacks 162 for item in items:
163 info_d = self.getInfos(item.entity, profile_key)
164 info_d.addCallbacks(infosCb, self._infosEb, [item.entity], None, [item.entity])
165 defers_list.append(info_d)
166 return defer.DeferredList(defers_list)
167
168 d = self.getItems(jid_, profile_key)
169 d.addCallback(gotItems)
170 d.addCallback(lambda dummy: found_entities)
171 return d
172
156 def findFeaturesSet(self, features, category=None, type_=None, jid_=None, profile_key=C.PROF_KEY_NONE): 173 def findFeaturesSet(self, features, category=None, type_=None, jid_=None, profile_key=C.PROF_KEY_NONE):
157 """Return entities (including jid_ and its items) offering features 174 """Return entities (including jid_ and its items) offering features
158 175
159 @param features: iterable of features which must be present 176 @param features: iterable of features which must be present
160 @param category: if not None, accept only this category 177 @param category: if not None, accept only this category
167 if jid_ is None: 184 if jid_ is None:
168 jid_ = jid.JID(client.jid.host) 185 jid_ = jid.JID(client.jid.host)
169 features = set(features) 186 features = set(features)
170 found_entities = set() 187 found_entities = set()
171 188
172 items = yield self.getItems(jid_, profile_key) 189 def infosCb(infos, entity):
173 for entity in [jid_] + [item.entity for item in items]:
174 infos = yield self.getInfos(entity, profile_key)
175 if category is not None or type_ is not None: 190 if category is not None or type_ is not None:
176 categories = set() 191 categories = set()
177 types = set() 192 types = set()
178 for identity in infos.identities: 193 for identity in infos.identities:
179 id_cat, id_type = identity 194 id_cat, id_type = identity
180 categories.add(id_cat) 195 categories.add(id_cat)
181 types.add(id_type) 196 types.add(id_type)
182 if category is not None and category not in categories: 197 if category is not None and category not in categories:
183 continue 198 return
184 if type_ is not None and type_ not in types: 199 if type_ is not None and type_ not in types:
185 continue 200 return
186 if features.issubset(infos.features): 201 if features.issubset(infos.features):
187 found_entities.add(entity) 202 found_entities.add(entity)
188 203
189 defer.returnValue(found_entities) 204 def gotItems(items):
205 defer_list = []
206 for entity in [jid_] + [item.entity for item in items]:
207 infos_d = self.getInfos(entity, profile_key)
208 infos_d.addCallbacks(infosCb, self._infosEb, [entity], None, [entity])
209 defer_list.append(infos_d)
210 return defer.DeferredList(defer_list)
211
212 d = self.getItems(jid_, profile_key)
213 d.addCallback(gotItems)
214 d.addCallback(lambda dummy: found_entities)
215 return d
190 216
191 def generateHash(self, services): 217 def generateHash(self, services):
192 """ Generate a unique hash for given service 218 """ Generate a unique hash for given service
193 219
194 hash algorithm is the one described in XEP-0115 220 hash algorithm is the one described in XEP-0115