Mercurial > libervia-backend
comparison sat/core/sat_main.py @ 2643:189e38fb11ff
core: style improvments (90 chars limit)
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 29 Jul 2018 18:44:27 +0200 |
parents | 56f94936df1e |
children | f2cf1daa42cb |
comparison
equal
deleted
inserted
replaced
2642:755a0b8643bd | 2643:189e38fb11ff |
---|---|
53 def __init__(self): | 53 def __init__(self): |
54 self._cb_map = {} # map from callback_id to callbacks | 54 self._cb_map = {} # map from callback_id to callbacks |
55 self._menus = ( | 55 self._menus = ( |
56 OrderedDict() | 56 OrderedDict() |
57 ) # dynamic menus. key: callback_id, value: menu data (dictionnary) | 57 ) # dynamic menus. key: callback_id, value: menu data (dictionnary) |
58 self._menus_paths = {} # path to id. key: (menu_type, lower case tuple of path), value: menu id | 58 self._menus_paths = {} # path to id. key: (menu_type, lower case tuple of path), |
59 # value: menu id | |
59 self.initialised = defer.Deferred() | 60 self.initialised = defer.Deferred() |
60 self.profiles = {} | 61 self.profiles = {} |
61 self.plugins = {} | 62 self.plugins = {} |
62 self.ns_map = { | 63 self.ns_map = { |
63 u"x-data": u"jabber:x:data" | 64 u"x-data": u"jabber:x:data" |
151 """Return the short version of SàT""" | 152 """Return the short version of SàT""" |
152 return C.APP_VERSION | 153 return C.APP_VERSION |
153 | 154 |
154 @property | 155 @property |
155 def full_version(self): | 156 def full_version(self): |
156 """Return the full version of SàT (with release name and extra data when in development mode)""" | 157 """Return the full version of SàT |
158 | |
159 In developement mode, release name and extra data are returned too | |
160 """ | |
157 version = self.version | 161 version = self.version |
158 if version[-1] == "D": | 162 if version[-1] == "D": |
159 # we are in debug version, we add extra data | 163 # we are in debug version, we add extra data |
160 try: | 164 try: |
161 return self._version_cache | 165 return self._version_cache |
199 def _import_plugins(self): | 203 def _import_plugins(self): |
200 """Import all plugins found in plugins directory""" | 204 """Import all plugins found in plugins directory""" |
201 # FIXME: module imported but cancelled should be deleted | 205 # FIXME: module imported but cancelled should be deleted |
202 # TODO: make this more generic and reusable in tools.common | 206 # TODO: make this more generic and reusable in tools.common |
203 # FIXME: should use imp | 207 # FIXME: should use imp |
204 # TODO: do not import all plugins if no needed: component plugins are not needed if we | 208 # TODO: do not import all plugins if no needed: component plugins are not needed |
205 # just use a client, and plugin blacklisting should be possible in sat.conf | 209 # if we just use a client, and plugin blacklisting should be possible in |
210 # sat.conf | |
206 plugins_path = os.path.dirname(sat.plugins.__file__) | 211 plugins_path = os.path.dirname(sat.plugins.__file__) |
207 plugin_glob = "plugin*." + C.PLUGIN_EXT | 212 plugin_glob = "plugin*." + C.PLUGIN_EXT |
208 plug_lst = [ | 213 plug_lst = [ |
209 os.path.splitext(plugin)[0] | 214 os.path.splitext(plugin)[0] |
210 for plugin in map( | 215 for plugin in map( |
217 try: | 222 try: |
218 __import__(plugin_path) | 223 __import__(plugin_path) |
219 except exceptions.MissingModule as e: | 224 except exceptions.MissingModule as e: |
220 self._unimport_plugin(plugin_path) | 225 self._unimport_plugin(plugin_path) |
221 log.warning( | 226 log.warning( |
222 u"Can't import plugin [{path}] because of an unavailale third party module:\n{msg}".format( | 227 u"Can't import plugin [{path}] because of an unavailale third party " |
228 u"module:\n{msg}".format( | |
223 path=plugin_path, msg=e | 229 path=plugin_path, msg=e |
224 ) | 230 ) |
225 ) | 231 ) |
226 continue | 232 continue |
227 except exceptions.CancelError as e: | 233 except exceptions.CancelError as e: |
263 continue | 269 continue |
264 | 270 |
265 if import_name in plugins_to_import: | 271 if import_name in plugins_to_import: |
266 log.error( | 272 log.error( |
267 _( | 273 _( |
268 u"Name conflict for import name [{import_name}], can't import plugin [{name}]" | 274 u"Name conflict for import name [{import_name}], can't import " |
275 u"plugin [{name}]" | |
269 ).format(**plugin_info) | 276 ).format(**plugin_info) |
270 ) | 277 ) |
271 continue | 278 continue |
272 plugins_to_import[import_name] = (plugin_path, mod, plugin_info) | 279 plugins_to_import[import_name] = (plugin_path, mod, plugin_info) |
273 while True: | 280 while True: |
281 def _import_plugins_from_dict( | 288 def _import_plugins_from_dict( |
282 self, plugins_to_import, import_name=None, optional=False | 289 self, plugins_to_import, import_name=None, optional=False |
283 ): | 290 ): |
284 """Recursively import and their dependencies in the right order | 291 """Recursively import and their dependencies in the right order |
285 | 292 |
286 @param plugins_to_import(dict): key=import_name and values=(plugin_path, module, plugin_info) | 293 @param plugins_to_import(dict): key=import_name and values=(plugin_path, module, |
287 @param import_name(unicode, None): name of the plugin to import as found in PLUGIN_INFO['import_name'] | 294 plugin_info) |
288 @param optional(bool): if False and plugin is not found, an ImportError exception is raised | 295 @param import_name(unicode, None): name of the plugin to import as found in |
296 PLUGIN_INFO['import_name'] | |
297 @param optional(bool): if False and plugin is not found, an ImportError exception | |
298 is raised | |
289 """ | 299 """ |
290 if import_name in self.plugins: | 300 if import_name in self.plugins: |
291 log.debug(u"Plugin {} already imported, passing".format(import_name)) | 301 log.debug(u"Plugin {} already imported, passing".format(import_name)) |
292 return | 302 return |
293 if not import_name: | 303 if not import_name: |
415 def getFeatures(self, profile_key=C.PROF_KEY_NONE): | 425 def getFeatures(self, profile_key=C.PROF_KEY_NONE): |
416 """Get available features | 426 """Get available features |
417 | 427 |
418 Return list of activated plugins and plugin specific data | 428 Return list of activated plugins and plugin specific data |
419 @param profile_key: %(doc_profile_key)s | 429 @param profile_key: %(doc_profile_key)s |
420 C.PROF_KEY_NONE can be used to have general plugins data (i.e. not profile dependent) | 430 C.PROF_KEY_NONE can be used to have general plugins data (i.e. not profile |
431 dependent) | |
421 @return (dict)[Deferred]: features data where: | 432 @return (dict)[Deferred]: features data where: |
422 - key is plugin import name, present only for activated plugins | 433 - key is plugin import name, present only for activated plugins |
423 - value is a an other dict, when meaning is specific to each plugin. | 434 - value is a an other dict, when meaning is specific to each plugin. |
424 this dict is return by plugin's getFeature method. | 435 this dict is return by plugin's getFeature method. |
425 If this method doesn't exists, an empty dict is returned. | 436 If this method doesn't exists, an empty dict is returned. |
532 return self.profiles[profile] | 543 return self.profiles[profile] |
533 except KeyError: | 544 except KeyError: |
534 raise exceptions.NotFound(profile_key) | 545 raise exceptions.NotFound(profile_key) |
535 | 546 |
536 def getClients(self, profile_key): | 547 def getClients(self, profile_key): |
537 """Convenient method to get list of clients from profile key (manage list through profile_key like C.PROF_KEY_ALL) | 548 """Convenient method to get list of clients from profile key |
538 | 549 |
550 Manage list through profile_key like C.PROF_KEY_ALL | |
539 @param profile_key: %(doc_profile_key)s | 551 @param profile_key: %(doc_profile_key)s |
540 @return: list of clients | 552 @return: list of clients |
541 """ | 553 """ |
542 if not profile_key: | 554 if not profile_key: |
543 raise exceptions.DataError(_(u"profile_key must not be empty")) | 555 raise exceptions.DataError(_(u"profile_key must not be empty")) |
597 @param component(bool): if True, path will be prefixed with C.COMPONENTS_DIR | 609 @param component(bool): if True, path will be prefixed with C.COMPONENTS_DIR |
598 @param profile(bool): if True, path will be suffixed by profile name | 610 @param profile(bool): if True, path will be suffixed by profile name |
599 @param *extra_path: extra path element(s) to use | 611 @param *extra_path: extra path element(s) to use |
600 @return (unicode): path | 612 @return (unicode): path |
601 """ | 613 """ |
602 # FIXME: component and profile are parsed with **kwargs because of python 2 limitations | 614 # FIXME: component and profile are parsed with **kwargs because of python 2 |
603 # once moved to python 3, this can be fixed | 615 # limitations. Once moved to python 3, this can be fixed |
604 component = kwargs.pop("component", False) | 616 component = kwargs.pop("component", False) |
605 profile = kwargs.pop("profile", True) | 617 profile = kwargs.pop("profile", True) |
606 assert not kwargs | 618 assert not kwargs |
607 | 619 |
608 path_elts = [self.memory.getConfig("", "local_dir")] | 620 path_elts = [self.memory.getConfig("", "local_dir")] |
637 return False | 649 return False |
638 return self.profiles[profile].isConnected() | 650 return self.profiles[profile].isConnected() |
639 | 651 |
640 ## XMPP methods ## | 652 ## XMPP methods ## |
641 | 653 |
642 def _messageSend( | 654 def _messageSend(self, to_jid_s, message, subject=None, mess_type="auto", extra=None, |
643 self, | 655 profile_key=C.PROF_KEY_NONE,): |
644 to_jid_s, | |
645 message, | |
646 subject=None, | |
647 mess_type="auto", | |
648 extra=None, | |
649 profile_key=C.PROF_KEY_NONE, | |
650 ): | |
651 client = self.getClient(profile_key) | 656 client = self.getClient(profile_key) |
652 to_jid = jid.JID(to_jid_s) | 657 to_jid = jid.JID(to_jid_s) |
653 # XXX: we need to use the dictionary comprehension because D-Bus return its own types, and pickle can't manage them. TODO: Need to find a better way | 658 # XXX: we need to use the dictionary comprehension because D-Bus return its own |
659 # types, and pickle can't manage them. TODO: Need to find a better way | |
654 return client.sendMessage( | 660 return client.sendMessage( |
655 to_jid, | 661 to_jid, |
656 message, | 662 message, |
657 subject, | 663 subject, |
658 mess_type, | 664 mess_type, |
672 assert profile | 678 assert profile |
673 priority = int( | 679 priority = int( |
674 self.memory.getParamA("Priority", "Connection", profile_key=profile) | 680 self.memory.getParamA("Priority", "Connection", profile_key=profile) |
675 ) | 681 ) |
676 self.profiles[profile].presence.available(to_jid, show, statuses, priority) | 682 self.profiles[profile].presence.available(to_jid, show, statuses, priority) |
677 # XXX: FIXME: temporary fix to work around openfire 3.7.0 bug (presence is not broadcasted to generating resource) | 683 # XXX: FIXME: temporary fix to work around openfire 3.7.0 bug (presence is not |
684 # broadcasted to generating resource) | |
678 if "" in statuses: | 685 if "" in statuses: |
679 statuses[C.PRESENCE_STATUSES_DEFAULT] = statuses.pop("") | 686 statuses[C.PRESENCE_STATUSES_DEFAULT] = statuses.pop("") |
680 self.bridge.presenceUpdate( | 687 self.bridge.presenceUpdate( |
681 self.profiles[profile].jid.full(), show, int(priority), statuses, profile | 688 self.profiles[profile].jid.full(), show, int(priority), statuses, profile |
682 ) | 689 ) |
707 | 714 |
708 def addContact(self, to_jid, profile_key): | 715 def addContact(self, to_jid, profile_key): |
709 """Add a contact in roster list""" | 716 """Add a contact in roster list""" |
710 profile = self.memory.getProfileName(profile_key) | 717 profile = self.memory.getProfileName(profile_key) |
711 assert profile | 718 assert profile |
712 # presence is sufficient, as a roster push will be sent according to RFC 6121 §3.1.2 | 719 # presence is sufficient, as a roster push will be sent according to |
720 # RFC 6121 §3.1.2 | |
713 self.profiles[profile].presence.subscribe(to_jid) | 721 self.profiles[profile].presence.subscribe(to_jid) |
714 | 722 |
715 def _updateContact(self, to_jid_s, name, groups, profile_key): | 723 def _updateContact(self, to_jid_s, name, groups, profile_key): |
716 return self.updateContact(jid.JID(to_jid_s), name, groups, profile_key) | 724 return self.updateContact(jid.JID(to_jid_s), name, groups, profile_key) |
717 | 725 |
799 local_device=False, | 807 local_device=False, |
800 ): | 808 ): |
801 """retrieve all services or contacts managing a set a features | 809 """retrieve all services or contacts managing a set a features |
802 | 810 |
803 @param namespaces(list[unicode]): features which must be handled | 811 @param namespaces(list[unicode]): features which must be handled |
804 @param identities(list[tuple[unicode,unicode]], None): if not None or empty, only keep those identities | 812 @param identities(list[tuple[unicode,unicode]], None): if not None or empty, |
805 tuple must by (category, type) | 813 only keep those identities tuple must by (category, type) |
806 @param bare_jids(bool): retrieve only bare_jids if True | 814 @param bare_jids(bool): retrieve only bare_jids if True |
807 if False, retrieve full jid of connected devices | 815 if False, retrieve full jid of connected devices |
808 @param service(bool): if True return service from our roster | 816 @param service(bool): if True return service from our roster |
809 @param roster(bool): if True, return entities in roster | 817 @param roster(bool): if True, return entities in roster |
810 full jid of all matching resources available will be returned | 818 full jid of all matching resources available will be returned |
811 @param own_jid(bool): if True, return profile's jid resources | 819 @param own_jid(bool): if True, return profile's jid resources |
812 @param local_device(bool): if True, return profile's jid local resource (i.e. client.jid) | 820 @param local_device(bool): if True, return profile's jid local resource |
813 @return (tuple(dict[jid.JID(), tuple[unicode, unicode, unicode]]*3)): found entities in a tuple with: | 821 (i.e. client.jid) |
822 @return (tuple(dict[jid.JID(), tuple[unicode, unicode, unicode]]*3)): found | |
823 entities in a tuple with: | |
814 - service entities | 824 - service entities |
815 - own entities | 825 - own entities |
816 - roster entities | 826 - roster entities |
817 """ | 827 """ |
818 if not identities: | 828 if not identities: |
894 ): | 904 ): |
895 """Shortcut to bridge.actionNew which generate and id and keep for retrieval | 905 """Shortcut to bridge.actionNew which generate and id and keep for retrieval |
896 | 906 |
897 @param action_data(dict): action data (see bridge documentation) | 907 @param action_data(dict): action data (see bridge documentation) |
898 @param security_limit: %(doc_security_limit)s | 908 @param security_limit: %(doc_security_limit)s |
899 @param keep_id(None, unicode): if not None, used to keep action for differed retrieval | 909 @param keep_id(None, unicode): if not None, used to keep action for differed |
900 must be set to the callback_id | 910 retrieval. Must be set to the callback_id. |
901 action will be deleted after 30 min. | 911 Action will be deleted after 30 min. |
902 @param profile: %(doc_profile)s | 912 @param profile: %(doc_profile)s |
903 """ | 913 """ |
904 id_ = unicode(uuid.uuid4()) | 914 id_ = unicode(uuid.uuid4()) |
905 if keep_id is not None: | 915 if keep_id is not None: |
906 client = self.getClient(profile) | 916 client = self.getClient(profile) |
946 @param progress_id(unicode): unique id of the progressing element | 956 @param progress_id(unicode): unique id of the progressing element |
947 @param profile: %(doc_profile)s | 957 @param profile: %(doc_profile)s |
948 @return (dict): data with the following keys: | 958 @return (dict): data with the following keys: |
949 'position' (int): current possition | 959 'position' (int): current possition |
950 'size' (int): end_position | 960 'size' (int): end_position |
951 if id doesn't exists (may be a finished progression), and empty dict is returned | 961 if id doesn't exists (may be a finished progression), and empty dict is |
962 returned | |
952 """ | 963 """ |
953 client = self.getClient(profile) | 964 client = self.getClient(profile) |
954 try: | 965 try: |
955 data = client._progress_cb[progress_id][0](progress_id, profile) | 966 data = client._progress_cb[progress_id][0](progress_id, profile) |
956 except KeyError: | 967 except KeyError: |
967 | 978 |
968 def progressGetAllMetadata(self, profile_key): | 979 def progressGetAllMetadata(self, profile_key): |
969 """Return all progress metadata at once | 980 """Return all progress metadata at once |
970 | 981 |
971 @param profile_key: %(doc_profile)s | 982 @param profile_key: %(doc_profile)s |
972 if C.PROF_KEY_ALL is used, all progress metadata from all profiles are returned | 983 if C.PROF_KEY_ALL is used, all progress metadata from all profiles are |
984 returned | |
973 @return (dict[dict[dict]]): a dict which map profile to progress_dict | 985 @return (dict[dict[dict]]): a dict which map profile to progress_dict |
974 progress_dict map progress_id to progress_data | 986 progress_dict map progress_id to progress_data |
975 progress_metadata is the same dict as sent by [progressStarted] | 987 progress_metadata is the same dict as sent by [progressStarted] |
976 """ | 988 """ |
977 clients = self.getClients(profile_key) | 989 clients = self.getClients(profile_key) |
1010 """Register a callback. | 1022 """Register a callback. |
1011 | 1023 |
1012 @param callback(callable): method to call | 1024 @param callback(callable): method to call |
1013 @param kwargs: can contain: | 1025 @param kwargs: can contain: |
1014 with_data(bool): True if the callback use the optional data dict | 1026 with_data(bool): True if the callback use the optional data dict |
1015 force_id(unicode): id to avoid generated id. Can lead to name conflict, avoid if possible | 1027 force_id(unicode): id to avoid generated id. Can lead to name conflict, avoid |
1028 if possible | |
1016 one_shot(bool): True to delete callback once it have been called | 1029 one_shot(bool): True to delete callback once it have been called |
1017 @return: id of the registered callback | 1030 @return: id of the registered callback |
1018 """ | 1031 """ |
1019 callback_id = kwargs.pop("force_id", None) | 1032 callback_id = kwargs.pop("force_id", None) |
1020 if callback_id is None: | 1033 if callback_id is None: |
1048 @param callback_id: id of the action (callback) to launch | 1061 @param callback_id: id of the action (callback) to launch |
1049 @param data: optional data | 1062 @param data: optional data |
1050 @profile_key: %(doc_profile_key)s | 1063 @profile_key: %(doc_profile_key)s |
1051 @return: a deferred which fire a dict where key can be: | 1064 @return: a deferred which fire a dict where key can be: |
1052 - xmlui: a XMLUI need to be displayed | 1065 - xmlui: a XMLUI need to be displayed |
1053 - validated: if present, can be used to launch a callback, it can have the values | 1066 - validated: if present, can be used to launch a callback, it can have the |
1067 values | |
1054 - C.BOOL_TRUE | 1068 - C.BOOL_TRUE |
1055 - C.BOOL_FALSE | 1069 - C.BOOL_FALSE |
1056 """ | 1070 """ |
1057 # FIXME: security limit need to be checked here | 1071 # FIXME: security limit need to be checked here |
1058 try: | 1072 try: |
1115 help_string="", | 1129 help_string="", |
1116 type_=C.MENU_GLOBAL, | 1130 type_=C.MENU_GLOBAL, |
1117 ): | 1131 ): |
1118 """register a new menu for frontends | 1132 """register a new menu for frontends |
1119 | 1133 |
1120 @param path(iterable[unicode]): path to go to the menu (category/subcategory/.../item) (e.g.: ("File", "Open")) | 1134 @param path(iterable[unicode]): path to go to the menu |
1135 (category/subcategory/.../item) (e.g.: ("File", "Open")) | |
1121 /!\ use D_() instead of _() for translations (e.g. (D_("File"), D_("Open"))) | 1136 /!\ use D_() instead of _() for translations (e.g. (D_("File"), D_("Open"))) |
1122 untranslated/lower case path can be used to identity a menu, for this reason it must be unique independently of case. | 1137 untranslated/lower case path can be used to identity a menu, for this reason |
1123 @param callback(callable): method to be called when menuitem is selected, callable or a callback id (string) as returned by [registerCallback] | 1138 it must be unique independently of case. |
1139 @param callback(callable): method to be called when menuitem is selected, callable | |
1140 or a callback id (string) as returned by [registerCallback] | |
1124 @param security_limit(int): %(doc_security_limit)s | 1141 @param security_limit(int): %(doc_security_limit)s |
1125 /!\ security_limit MUST be added to data in launchCallback if used #TODO | 1142 /!\ security_limit MUST be added to data in launchCallback if used #TODO |
1126 @param help_string(unicode): string used to indicate what the menu do (can be show as a tooltip). | 1143 @param help_string(unicode): string used to indicate what the menu do (can be |
1144 show as a tooltip). | |
1127 /!\ use D_() instead of _() for translations | 1145 /!\ use D_() instead of _() for translations |
1128 @param type(unicode): one of: | 1146 @param type(unicode): one of: |
1129 - C.MENU_GLOBAL: classical menu, can be shown in a menubar on top (e.g. something like File/Open) | 1147 - C.MENU_GLOBAL: classical menu, can be shown in a menubar on top (e.g. |
1148 something like File/Open) | |
1130 - C.MENU_ROOM: like a global menu, but only shown in multi-user chat | 1149 - C.MENU_ROOM: like a global menu, but only shown in multi-user chat |
1131 menu_data must contain a "room_jid" data | 1150 menu_data must contain a "room_jid" data |
1132 - C.MENU_SINGLE: like a global menu, but only shown in one2one chat | 1151 - C.MENU_SINGLE: like a global menu, but only shown in one2one chat |
1133 menu_data must contain a "jid" data | 1152 menu_data must contain a "jid" data |
1134 - C.MENU_JID_CONTEXT: contextual menu, used with any jid (e.g.: ad hoc commands, jid is already filled) | 1153 - C.MENU_JID_CONTEXT: contextual menu, used with any jid (e.g.: ad hoc |
1154 commands, jid is already filled) | |
1135 menu_data must contain a "jid" data | 1155 menu_data must contain a "jid" data |
1136 - C.MENU_ROSTER_JID_CONTEXT: like JID_CONTEXT, but restricted to jids in roster. | 1156 - C.MENU_ROSTER_JID_CONTEXT: like JID_CONTEXT, but restricted to jids in |
1157 roster. | |
1137 menu_data must contain a "room_jid" data | 1158 menu_data must contain a "room_jid" data |
1138 - C.MENU_ROSTER_GROUP_CONTEXT: contextual menu, used with group (e.g.: publish microblog, group is already filled) | 1159 - C.MENU_ROSTER_GROUP_CONTEXT: contextual menu, used with group (e.g.: publish |
1160 microblog, group is already filled) | |
1139 menu_data must contain a "group" data | 1161 menu_data must contain a "group" data |
1140 @return (unicode): menu_id (same as callback_id) | 1162 @return (unicode): menu_id (same as callback_id) |
1141 """ | 1163 """ |
1142 | 1164 |
1143 if callable(callback): | 1165 if callable(callback): |