comparison sat/plugins/plugin_import.py @ 4037:524856bd7b19

massive refactoring to switch from camelCase to snake_case: historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a pre-PEP8 code, to use the same coding style as in Twisted. However, snake_case is more readable and it's better to follow PEP8 best practices, so it has been decided to move on full snake_case. Because Libervia has a huge codebase, this ended with a ugly mix of camelCase and snake_case. To fix that, this patch does a big refactoring by renaming every function and method (including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case. This is a massive change, and may result in some bugs.
author Goffi <goffi@goffi.org>
date Sat, 08 Apr 2023 13:54:42 +0200
parents 04283582966f
children
comparison
equal deleted inserted replaced
4036:c4464d7ae97b 4037:524856bd7b19
44 Importer = collections.namedtuple("Importer", ("callback", "short_desc", "long_desc")) 44 Importer = collections.namedtuple("Importer", ("callback", "short_desc", "long_desc"))
45 45
46 46
47 class ImportPlugin(object): 47 class ImportPlugin(object):
48 def __init__(self, host): 48 def __init__(self, host):
49 log.info(_("plugin Import initialization")) 49 log.info(_("plugin import initialization"))
50 self.host = host 50 self.host = host
51 51
52 def initialize(self, import_handler, name): 52 def initialize(self, import_handler, name):
53 """Initialize a specialized import handler 53 """Initialize a specialized import handler
54 54
55 @param import_handler(object): specialized import handler instance 55 @param import_handler(object): specialized import handler instance
56 must have the following methods: 56 must have the following methods:
57 - importItem: import a single main item (i.e. prepare data for publishing) 57 - import_item: import a single main item (i.e. prepare data for publishing)
58 - importSubitems: import sub items (i.e. items linked to main item, e.g. comments). 58 - importSubitems: import sub items (i.e. items linked to main item, e.g. comments).
59 Must return a dict with kwargs for recursiveImport if items are to be imported recursively. 59 Must return a dict with kwargs for recursive_import if items are to be imported recursively.
60 At least "items_import_data", "service" and "node" keys must be provided. 60 At least "items_import_data", "service" and "node" keys must be provided.
61 if None is returned, no recursion will be done to import subitems, but import can still be done directly by the method. 61 if None is returned, no recursion will be done to import subitems, but import can still be done directly by the method.
62 - publishItem: actualy publish an item 62 - publish_item: actualy publish an item
63 - itemFilters: modify item according to options 63 - item_filters: modify item according to options
64 @param name(unicode): import handler name 64 @param name(unicode): import handler name
65 """ 65 """
66 assert name == name.lower().strip() 66 assert name == name.lower().strip()
67 log.info(_("initializing {name} import handler").format(name=name)) 67 log.info(_("initializing {name} import handler").format(name=name))
68 import_handler.name = name 68 import_handler.name = name
69 import_handler.register = partial(self.register, import_handler) 69 import_handler.register = partial(self.register, import_handler)
70 import_handler.unregister = partial(self.unregister, import_handler) 70 import_handler.unregister = partial(self.unregister, import_handler)
71 import_handler.importers = {} 71 import_handler.importers = {}
72 72
73 def _import(name, location, options, pubsub_service, pubsub_node, profile): 73 def _import(name, location, options, pubsub_service, pubsub_node, profile):
74 return self._doImport( 74 return self._do_import(
75 import_handler, 75 import_handler,
76 name, 76 name,
77 location, 77 location,
78 options, 78 options,
79 pubsub_service, 79 pubsub_service,
80 pubsub_node, 80 pubsub_node,
81 profile, 81 profile,
82 ) 82 )
83 83
84 def _importList(): 84 def _import_list():
85 return self.listImporters(import_handler) 85 return self.list_importers(import_handler)
86 86
87 def _importDesc(name): 87 def _import_desc(name):
88 return self.getDescription(import_handler, name) 88 return self.getDescription(import_handler, name)
89 89
90 self.host.bridge.addMethod( 90 self.host.bridge.add_method(
91 name + "Import", 91 name + "import",
92 ".plugin", 92 ".plugin",
93 in_sign="ssa{ss}sss", 93 in_sign="ssa{ss}sss",
94 out_sign="s", 94 out_sign="s",
95 method=_import, 95 method=_import,
96 async_=True, 96 async_=True,
97 ) 97 )
98 self.host.bridge.addMethod( 98 self.host.bridge.add_method(
99 name + "ImportList", 99 name + "ImportList",
100 ".plugin", 100 ".plugin",
101 in_sign="", 101 in_sign="",
102 out_sign="a(ss)", 102 out_sign="a(ss)",
103 method=_importList, 103 method=_import_list,
104 ) 104 )
105 self.host.bridge.addMethod( 105 self.host.bridge.add_method(
106 name + "ImportDesc", 106 name + "ImportDesc",
107 ".plugin", 107 ".plugin",
108 in_sign="s", 108 in_sign="s",
109 out_sign="(ss)", 109 out_sign="(ss)",
110 method=_importDesc, 110 method=_import_desc,
111 ) 111 )
112 112
113 def getProgress(self, import_handler, progress_id, profile): 113 def get_progress(self, import_handler, progress_id, profile):
114 client = self.host.getClient(profile) 114 client = self.host.get_client(profile)
115 return client._import[import_handler.name][progress_id] 115 return client._import[import_handler.name][progress_id]
116 116
117 def listImporters(self, import_handler): 117 def list_importers(self, import_handler):
118 importers = list(import_handler.importers.keys()) 118 importers = list(import_handler.importers.keys())
119 importers.sort() 119 importers.sort()
120 return [ 120 return [
121 (name, import_handler.importers[name].short_desc) 121 (name, import_handler.importers[name].short_desc)
122 for name in import_handler.importers 122 for name in import_handler.importers
137 ) 137 )
138 ) 138 )
139 else: 139 else:
140 return importer.short_desc, importer.long_desc 140 return importer.short_desc, importer.long_desc
141 141
142 def _doImport(self, import_handler, name, location, options, pubsub_service="", 142 def _do_import(self, import_handler, name, location, options, pubsub_service="",
143 pubsub_node="", profile=C.PROF_KEY_NONE): 143 pubsub_node="", profile=C.PROF_KEY_NONE):
144 client = self.host.getClient(profile) 144 client = self.host.get_client(profile)
145 options = {key: str(value) for key, value in options.items()} 145 options = {key: str(value) for key, value in options.items()}
146 for option in import_handler.BOOL_OPTIONS: 146 for option in import_handler.BOOL_OPTIONS:
147 try: 147 try:
148 options[option] = C.bool(options[option]) 148 options[option] = C.bool(options[option])
149 except KeyError: 149 except KeyError:
156 except ValueError: 156 except ValueError:
157 raise exceptions.DataError( 157 raise exceptions.DataError(
158 _("invalid json option: {option}").format(option=option) 158 _("invalid json option: {option}").format(option=option)
159 ) 159 )
160 pubsub_service = jid.JID(pubsub_service) if pubsub_service else None 160 pubsub_service = jid.JID(pubsub_service) if pubsub_service else None
161 return self.doImport( 161 return self.do_import(
162 client, 162 client,
163 import_handler, 163 import_handler,
164 str(name), 164 str(name),
165 str(location), 165 str(location),
166 options, 166 options,
167 pubsub_service, 167 pubsub_service,
168 pubsub_node or None, 168 pubsub_node or None,
169 ) 169 )
170 170
171 @defer.inlineCallbacks 171 @defer.inlineCallbacks
172 def doImport(self, client, import_handler, name, location, options=None, 172 def do_import(self, client, import_handler, name, location, options=None,
173 pubsub_service=None, pubsub_node=None,): 173 pubsub_service=None, pubsub_node=None,):
174 """Import data 174 """import data
175 175
176 @param import_handler(object): instance of the import handler 176 @param import_handler(object): instance of the import handler
177 @param name(unicode): name of the importer 177 @param name(unicode): name of the importer
178 @param location(unicode): location of the data to import 178 @param location(unicode): location of the data to import
179 can be an url, a file path, or anything which make sense 179 can be an url, a file path, or anything which make sense
219 metadata = { 219 metadata = {
220 "name": "{}: {}".format(name, location), 220 "name": "{}: {}".format(name, location),
221 "direction": "out", 221 "direction": "out",
222 "type": import_handler.name.upper() + "_IMPORT", 222 "type": import_handler.name.upper() + "_IMPORT",
223 } 223 }
224 self.host.registerProgressCb( 224 self.host.register_progress_cb(
225 progress_id, 225 progress_id,
226 partial(self.getProgress, import_handler), 226 partial(self.get_progress, import_handler),
227 metadata, 227 metadata,
228 profile=client.profile, 228 profile=client.profile,
229 ) 229 )
230 self.host.bridge.progressStarted(progress_id, metadata, client.profile) 230 self.host.bridge.progress_started(progress_id, metadata, client.profile)
231 session = { #  session data, can be used by importers 231 session = { #  session data, can be used by importers
232 "root_service": pubsub_service, 232 "root_service": pubsub_service,
233 "root_node": pubsub_node, 233 "root_node": pubsub_node,
234 } 234 }
235 self.recursiveImport( 235 self.recursive_import(
236 client, 236 client,
237 import_handler, 237 import_handler,
238 items_import_data, 238 items_import_data,
239 progress_id, 239 progress_id,
240 session, 240 session,
244 pubsub_node, 244 pubsub_node,
245 ) 245 )
246 defer.returnValue(progress_id) 246 defer.returnValue(progress_id)
247 247
248 @defer.inlineCallbacks 248 @defer.inlineCallbacks
249 def recursiveImport( 249 def recursive_import(
250 self, 250 self,
251 client, 251 client,
252 import_handler, 252 import_handler,
253 items_import_data, 253 items_import_data,
254 progress_id, 254 progress_id,
266 @param progress_id(unicode): id of progression 266 @param progress_id(unicode): id of progression
267 @param session(dict): data for this import session 267 @param session(dict): data for this import session
268 can be used by importer so store any useful data 268 can be used by importer so store any useful data
269 "root_service" and "root_node" are set to the main pubsub service and node of the import 269 "root_service" and "root_node" are set to the main pubsub service and node of the import
270 @param options(dict): import options 270 @param options(dict): import options
271 @param return_data(dict): data to return on progressFinished 271 @param return_data(dict): data to return on progress_finished
272 @param service(jid.JID, None): PubSub service to use 272 @param service(jid.JID, None): PubSub service to use
273 @param node(unicode, None): PubSub node to use 273 @param node(unicode, None): PubSub node to use
274 @param depth(int): level of recursion 274 @param depth(int): level of recursion
275 """ 275 """
276 if return_data is None: 276 if return_data is None:
277 return_data = {} 277 return_data = {}
278 for idx, item_import_data in enumerate(items_import_data): 278 for idx, item_import_data in enumerate(items_import_data):
279 item_data = yield import_handler.importItem( 279 item_data = yield import_handler.import_item(
280 client, item_import_data, session, options, return_data, service, node 280 client, item_import_data, session, options, return_data, service, node
281 ) 281 )
282 yield import_handler.itemFilters(client, item_data, session, options) 282 yield import_handler.item_filters(client, item_data, session, options)
283 recurse_kwargs = yield import_handler.importSubItems( 283 recurse_kwargs = yield import_handler.import_sub_items(
284 client, item_import_data, item_data, session, options 284 client, item_import_data, item_data, session, options
285 ) 285 )
286 yield import_handler.publishItem(client, item_data, service, node, session) 286 yield import_handler.publish_item(client, item_data, service, node, session)
287 287
288 if recurse_kwargs is not None: 288 if recurse_kwargs is not None:
289 recurse_kwargs["client"] = client 289 recurse_kwargs["client"] = client
290 recurse_kwargs["import_handler"] = import_handler 290 recurse_kwargs["import_handler"] = import_handler
291 recurse_kwargs["progress_id"] = progress_id 291 recurse_kwargs["progress_id"] = progress_id
292 recurse_kwargs["session"] = session 292 recurse_kwargs["session"] = session
293 recurse_kwargs.setdefault("options", options) 293 recurse_kwargs.setdefault("options", options)
294 recurse_kwargs["return_data"] = return_data 294 recurse_kwargs["return_data"] = return_data
295 recurse_kwargs["depth"] = depth + 1 295 recurse_kwargs["depth"] = depth + 1
296 log.debug(_("uploading subitems")) 296 log.debug(_("uploading subitems"))
297 yield self.recursiveImport(**recurse_kwargs) 297 yield self.recursive_import(**recurse_kwargs)
298 298
299 if depth == 0: 299 if depth == 0:
300 client._import[import_handler.name][progress_id]["position"] = str( 300 client._import[import_handler.name][progress_id]["position"] = str(
301 idx + 1 301 idx + 1
302 ) 302 )
303 303
304 if depth == 0: 304 if depth == 0:
305 self.host.bridge.progressFinished(progress_id, return_data, client.profile) 305 self.host.bridge.progress_finished(progress_id, return_data, client.profile)
306 self.host.removeProgressCb(progress_id, client.profile) 306 self.host.remove_progress_cb(progress_id, client.profile)
307 del client._import[import_handler.name][progress_id] 307 del client._import[import_handler.name][progress_id]
308 308
309 def register(self, import_handler, name, callback, short_desc="", long_desc=""): 309 def register(self, import_handler, name, callback, short_desc="", long_desc=""):
310 """Register an Importer method 310 """Register an Importer method
311 311
312 @param name(unicode): unique importer name, should indicate the software it can import and always lowercase 312 @param name(unicode): unique importer name, should indicate the software it can import and always lowercase
313 @param callback(callable): method to call: 313 @param callback(callable): method to call:
314 the signature must be (client, location, options) (cf. [doImport]) 314 the signature must be (client, location, options) (cf. [do_import])
315 the importer must return a tuple with (items_import_data, items_count) 315 the importer must return a tuple with (items_import_data, items_count)
316 items_import_data(iterable[dict]) data specific to specialized importer 316 items_import_data(iterable[dict]) data specific to specialized importer
317 cf. importItem docstring of specialized importer for details 317 cf. import_item docstring of specialized importer for details
318 items_count (int, None) indicate the total number of items (without subitems) 318 items_count (int, None) indicate the total number of items (without subitems)
319 useful to display a progress indicator when the iterator is a generator 319 useful to display a progress indicator when the iterator is a generator
320 use None if you can't guess the total number of items 320 use None if you can't guess the total number of items
321 @param short_desc(unicode): one line description of the importer 321 @param short_desc(unicode): one line description of the importer
322 @param long_desc(unicode): long description of the importer, its options, etc. 322 @param long_desc(unicode): long description of the importer, its options, etc.