comparison sat/tools/common/data_objects.py @ 3028:ab2696e34d29

Python 3 port: /!\ this is a huge commit /!\ starting from this commit, SàT is needs Python 3.6+ /!\ SàT maybe be instable or some feature may not work anymore, this will improve with time This patch port backend, bridge and frontends to Python 3. Roughly this has been done this way: - 2to3 tools has been applied (with python 3.7) - all references to python2 have been replaced with python3 (notably shebangs) - fixed files not handled by 2to3 (notably the shell script) - several manual fixes - fixed issues reported by Python 3 that where not handled in Python 2 - replaced "async" with "async_" when needed (it's a reserved word from Python 3.7) - replaced zope's "implements" with @implementer decorator - temporary hack to handle data pickled in database, as str or bytes may be returned, to be checked later - fixed hash comparison for password - removed some code which is not needed anymore with Python 3 - deactivated some code which needs to be checked (notably certificate validation) - tested with jp, fixed reported issues until some basic commands worked - ported Primitivus (after porting dependencies like urwid satext) - more manual fixes
author Goffi <goffi@goffi.org>
date Tue, 13 Aug 2019 19:08:41 +0200
parents 8dbef2d190eb
children 9d0df638c8b4
comparison
equal deleted inserted replaced
3027:ff5bcb12ae60 3028:ab2696e34d29
1 #!/usr/bin/env python2 1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 3
4 # SAT: a jabber client 4 # SAT: a jabber client
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org)
6 6
23 from sat.tools.common import data_format 23 from sat.tools.common import data_format
24 24
25 try: 25 try:
26 from jinja2 import Markup as safe 26 from jinja2 import Markup as safe
27 except ImportError: 27 except ImportError:
28 safe = unicode 28 safe = str
29 29
30 from sat.tools.common import uri as xmpp_uri 30 from sat.tools.common import uri as xmpp_uri
31 import urllib 31 import urllib.request, urllib.parse, urllib.error
32 32
33 q = lambda value: urllib.quote(value.encode("utf-8"), safe="@") 33 q = lambda value: urllib.parse.quote(value.encode("utf-8"), safe="@")
34 34
35 35
36 def parsePubSubMetadata(metadata, items): 36 def parsePubSubMetadata(metadata, items):
37 """Helper method to have nicer metadata while doing a PubSub request 37 """Helper method to have nicer metadata while doing a PubSub request
38 38
47 @return (dict): parsed metadata 47 @return (dict): parsed metadata
48 """ 48 """
49 data = {} 49 data = {}
50 assert "complete" not in metadata 50 assert "complete" not in metadata
51 51
52 for key, value in metadata.iteritems(): 52 for key, value in metadata.items():
53 if key in (u"rsm_index", u"rsm_count"): 53 if key in ("rsm_index", "rsm_count"):
54 value = int(value) 54 value = int(value)
55 elif key == u"mam_stable": 55 elif key == "mam_stable":
56 value = C.bool(value) 56 value = C.bool(value)
57 elif key == u"mam_complete": 57 elif key == "mam_complete":
58 key = u"complete" 58 key = "complete"
59 value = C.bool(value) 59 value = C.bool(value)
60 data[key] = value 60 data[key] = value
61 if u"complete" not in data: 61 if "complete" not in data:
62 index = data.get(u"rsm_index") 62 index = data.get("rsm_index")
63 count = data.get(u"rsm_count") 63 count = data.get("rsm_count")
64 if index is None or count is None: 64 if index is None or count is None:
65 # we don't have enough information to know if the data is complete or not 65 # we don't have enough information to know if the data is complete or not
66 data[u"complete"] = None 66 data["complete"] = None
67 else: 67 else:
68 # normally we have a strict equality here but XEP-0059 states 68 # normally we have a strict equality here but XEP-0059 states
69 # that index MAY be approximative, so just in case… 69 # that index MAY be approximative, so just in case…
70 data[u"complete"] = index + len(items) >= count 70 data["complete"] = index + len(items) >= count
71 return data 71 return data
72 72
73 73
74 class BlogItem(object): 74 class BlogItem(object):
75 def __init__(self, mb_data, parent, deserialise=True): 75 def __init__(self, mb_data, parent, deserialise=True):
86 self._comments = None 86 self._comments = None
87 self._comments_items_list = None 87 self._comments_items_list = None
88 88
89 @property 89 @property
90 def id(self): 90 def id(self):
91 return self.mb_data.get(u"id") 91 return self.mb_data.get("id")
92 92
93 @property 93 @property
94 def atom_id(self): 94 def atom_id(self):
95 return self.mb_data.get(u"atom_id") 95 return self.mb_data.get("atom_id")
96 96
97 @property 97 @property
98 def uri(self): 98 def uri(self):
99 node = self.parent.node 99 node = self.parent.node
100 service = self.parent.service 100 service = self.parent.service
101 return xmpp_uri.buildXMPPUri( 101 return xmpp_uri.buildXMPPUri(
102 u"pubsub", subtype=u"microblog", path=service, node=node, item=self.id 102 "pubsub", subtype="microblog", path=service, node=node, item=self.id
103 ) 103 )
104 104
105 @property 105 @property
106 def published(self): 106 def published(self):
107 return self.mb_data.get(u"published") 107 return self.mb_data.get("published")
108 108
109 @property 109 @property
110 def updated(self): 110 def updated(self):
111 return self.mb_data.get(u"updated") 111 return self.mb_data.get("updated")
112 112
113 @property 113 @property
114 def language(self): 114 def language(self):
115 return self.mb_data.get(u"language") 115 return self.mb_data.get("language")
116 116
117 @property 117 @property
118 def author(self): 118 def author(self):
119 return self.mb_data.get(u"author") 119 return self.mb_data.get("author")
120 120
121 @property 121 @property
122 def author_jid(self): 122 def author_jid(self):
123 return self.mb_data.get(u"author_jid") 123 return self.mb_data.get("author_jid")
124 124
125 @property 125 @property
126 def author_jid_verified(self): 126 def author_jid_verified(self):
127 return self.mb_data.get(u"author_jid_verified") 127 return self.mb_data.get("author_jid_verified")
128 128
129 @property 129 @property
130 def author_email(self): 130 def author_email(self):
131 return self.mb_data.get(u"author_email") 131 return self.mb_data.get("author_email")
132 132
133 @property 133 @property
134 def tags(self): 134 def tags(self):
135 return self.mb_data.get(u'tags', []) 135 return self.mb_data.get('tags', [])
136 136
137 @property 137 @property
138 def groups(self): 138 def groups(self):
139 return self.mb_data.get(u'groups', []) 139 return self.mb_data.get('groups', [])
140 140
141 @property 141 @property
142 def title(self): 142 def title(self):
143 return self.mb_data.get(u"title") 143 return self.mb_data.get("title")
144 144
145 @property 145 @property
146 def title_xhtml(self): 146 def title_xhtml(self):
147 try: 147 try:
148 return safe(self.mb_data[u"title_xhtml"]) 148 return safe(self.mb_data["title_xhtml"])
149 except KeyError: 149 except KeyError:
150 return None 150 return None
151 151
152 @property 152 @property
153 def content(self): 153 def content(self):
154 return self.mb_data.get(u"content") 154 return self.mb_data.get("content")
155 155
156 @property 156 @property
157 def content_xhtml(self): 157 def content_xhtml(self):
158 try: 158 try:
159 return safe(self.mb_data[u"content_xhtml"]) 159 return safe(self.mb_data["content_xhtml"])
160 except KeyError: 160 except KeyError:
161 return None 161 return None
162 162
163 @property 163 @property
164 def comments(self): 164 def comments(self):
165 if self._comments is None: 165 if self._comments is None:
166 self._comments = data_format.dict2iterdict( 166 self._comments = data_format.dict2iterdict(
167 u"comments", self.mb_data, (u"node", u"service") 167 "comments", self.mb_data, ("node", "service")
168 ) 168 )
169 return self._comments 169 return self._comments
170 170
171 @property 171 @property
172 def comments_service(self): 172 def comments_service(self):
173 return self.mb_data.get(u"comments_service") 173 return self.mb_data.get("comments_service")
174 174
175 @property 175 @property
176 def comments_node(self): 176 def comments_node(self):
177 return self.mb_data.get(u"comments_node") 177 return self.mb_data.get("comments_node")
178 178
179 @property 179 @property
180 def comments_items_list(self): 180 def comments_items_list(self):
181 return [] if self._comments_items_list is None else self._comments_items_list 181 return [] if self._comments_items_list is None else self._comments_items_list
182 182
197 self.items = [BlogItem(i, self, deserialise=deserialise) for i in mb_data[0]] 197 self.items = [BlogItem(i, self, deserialise=deserialise) for i in mb_data[0]]
198 self.metadata = parsePubSubMetadata(mb_data[1], self.items) 198 self.metadata = parsePubSubMetadata(mb_data[1], self.items)
199 199
200 @property 200 @property
201 def service(self): 201 def service(self):
202 return self.metadata[u"service"] 202 return self.metadata["service"]
203 203
204 @property 204 @property
205 def node(self): 205 def node(self):
206 return self.metadata[u"node"] 206 return self.metadata["node"]
207 207
208 @property 208 @property
209 def uri(self): 209 def uri(self):
210 return self.metadata[u"uri"] 210 return self.metadata["uri"]
211 211
212 @property 212 @property
213 def with_rsm(self): 213 def with_rsm(self):
214 """Return True if RSM is activated on this request""" 214 """Return True if RSM is activated on this request"""
215 return u"rsm_first" in self.metadata 215 return "rsm_first" in self.metadata
216 216
217 @property 217 @property
218 def rsm_first(self): 218 def rsm_first(self):
219 return self.metadata[u"rsm_first"] 219 return self.metadata["rsm_first"]
220 220
221 @property 221 @property
222 def rsm_last(self): 222 def rsm_last(self):
223 return self.metadata[u"rsm_last"] 223 return self.metadata["rsm_last"]
224 224
225 @property 225 @property
226 def rsm_index(self): 226 def rsm_index(self):
227 return self.metadata[u"rsm_index"] 227 return self.metadata["rsm_index"]
228 228
229 @property 229 @property
230 def rsm_count(self): 230 def rsm_count(self):
231 return self.metadata[u"rsm_count"] 231 return self.metadata["rsm_count"]
232 232
233 @property 233 @property
234 def complete(self): 234 def complete(self):
235 return self.metadata[u"complete"] 235 return self.metadata["complete"]
236 236
237 def __len__(self): 237 def __len__(self):
238 return self.items.__len__() 238 return self.items.__len__()
239 239
240 def __missing__(self, key): 240 def __missing__(self, key):
280 @property 280 @property
281 def text(self): 281 def text(self):
282 try: 282 try:
283 return self._message_data[""] 283 return self._message_data[""]
284 except KeyError: 284 except KeyError:
285 return next(self._message_data.itervalues()) 285 return next(iter(self._message_data.values()))
286 286
287 @property 287 @property
288 def subject(self): 288 def subject(self):
289 try: 289 try:
290 return self._subject_data[""] 290 return self._subject_data[""]
291 except KeyError: 291 except KeyError:
292 return next(self._subject_data.itervalues()) 292 return next(iter(self._subject_data.values()))
293 293
294 @property 294 @property
295 def type(self): 295 def type(self):
296 return self._type 296 return self._type
297 297
320 if not self._html: 320 if not self._html:
321 return None 321 return None
322 try: 322 try:
323 return safe(self._html[""]) 323 return safe(self._html[""])
324 except KeyError: 324 except KeyError:
325 return safe(next(self._html.itervalues())) 325 return safe(next(iter(self._html.values())))
326 326
327 327
328 class Messages(object): 328 class Messages(object):
329 def __init__(self, msgs_data): 329 def __init__(self, msgs_data):
330 self.messages = [Message(m) for m in msgs_data] 330 self.messages = [Message(m) for m in msgs_data]
417 """format URL using Python formatting 417 """format URL using Python formatting
418 418
419 values will be quoted before being used 419 values will be quoted before being used
420 """ 420 """
421 return self.url.format( 421 return self.url.format(
422 *[q(a) for a in args], **{k: ObjectQuoter(v) for k, v in kwargs.iteritems()} 422 *[q(a) for a in args], **{k: ObjectQuoter(v) for k, v in kwargs.items()}
423 ) 423 )