Mercurial > libervia-backend
diff sat/plugins/plugin_misc_file.py @ 3403:404d4b29de52
plugin file, XEP-0234: registering is now done by class + use of async:
- instead of registering a callback, a file sending manager now register itself and must
implement some well known method (`fileSend`, `canHandleFileSend`) and optionally a
`name` attribute
- `utils.asDeferred` is now used for callbacks, so all type of methods including
coroutines can be used.
- feature checking is now handled by `canHandleFileSend` method instead of simple
namespace check, this allows to use a method when namespace can't be checked (this is
the case when a file is sent to a bare jid with jingle)
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 12 Nov 2020 14:53:15 +0100 |
parents | f2bb57348587 |
children | be6d91572633 |
line wrap: on
line diff
--- a/sat/plugins/plugin_misc_file.py Thu Nov 12 14:53:15 2020 +0100 +++ b/sat/plugins/plugin_misc_file.py Thu Nov 12 14:53:15 2020 +0100 @@ -17,18 +17,21 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import os +import os.path +from functools import partial +from twisted.internet import defer +from twisted.words.protocols.jabber import jid from sat.core.i18n import _, D_ from sat.core.constants import Const as C from sat.core.log import getLogger - -log = getLogger(__name__) from sat.core import exceptions from sat.tools import xml_tools from sat.tools import stream -from twisted.internet import defer -from twisted.words.protocols.jabber import jid -import os -import os.path +from sat.tools import utils + + +log = getLogger(__name__) PLUGIN_INFO = { @@ -73,7 +76,7 @@ method=self._fileSend, async_=True, ) - self._file_callbacks = [] + self._file_managers = [] host.importMenu( (D_("Action"), D_("send file")), self._fileSendMenu, @@ -85,12 +88,11 @@ def _fileSend(self, peer_jid_s, filepath, name="", file_desc="", extra=None, profile=C.PROF_KEY_NONE): client = self.host.getClient(profile) - return self.fileSend( + return defer.ensureDeferred(self.fileSend( client, jid.JID(peer_jid_s), filepath, name or None, file_desc or None, extra - ) + )) - @defer.inlineCallbacks - def fileSend( + async def fileSend( self, client, peer_jid, filepath, filename=None, file_desc=None, extra=None ): """Send a file using best available method @@ -107,29 +109,45 @@ raise exceptions.DataError("The given path doesn't link to a file") if not filename: filename = os.path.basename(filepath) or "_" - for namespace, callback, priority, method_name in self._file_callbacks: - has_feature = yield self.host.hasFeature(client, namespace, peer_jid) - if has_feature: + for manager, priority in self._file_managers: + if await utils.asDeferred(manager.canHandleFileSend, + client, peer_jid, filepath): + try: + method_name = manager.name + except AttributeError: + method_name = manager.__class__.__name__ log.info( - "{name} method will be used to send the file".format( + _("{name} method will be used to send the file").format( name=method_name ) ) - progress_id = yield callback( - client, peer_jid, filepath, filename, file_desc, extra - ) - defer.returnValue({"progress": progress_id}) + try: + progress_id = await utils.asDeferred( + manager.fileSend, client, peer_jid, filepath, filename, file_desc, + extra + ) + except Exception as e: + log.warning( + _("Can't send {filepath} to {peer_jid} with {method_name}: " + "{reason}").format( + filepath=filepath, + peer_jid=peer_jid, + method_name=method_name, + reason=e + ) + ) + continue + return {"progress": progress_id} msg = "Can't find any method to send file to {jid}".format(jid=peer_jid.full()) log.warning(msg) - defer.returnValue( - { - "xmlui": xml_tools.note( - "Can't transfer file", msg, C.XMLUI_DATA_LVL_WARNING - ).toXml() - } - ) + return { + "xmlui": xml_tools.note( + "Can't transfer file", msg, C.XMLUI_DATA_LVL_WARNING + ).toXml() + } - def _onFileChoosed(self, client, peer_jid, data): + def _onFileChoosed(self, peer_jid, data, profile): + client = self.host.getClient(profile) cancelled = C.bool(data.get("cancelled", C.BOOL_FALSE)) if cancelled: return @@ -147,9 +165,7 @@ raise exceptions.DataError(_("Invalid JID")) file_choosed_id = self.host.registerCallback( - lambda data, profile: self._onFileChoosed( - self.host.getClient(profile), jid_, data - ), + partial(self._onFileChoosed, jid_), with_data=True, one_shot=True, ) @@ -165,30 +181,31 @@ return {"xmlui": xml_ui.toXml()} - def register(self, namespace, callback, priority=0, method_name=None): - """Register a fileSending method + def register(self, manager, priority: int = 0) -> None: + """Register a fileSending manager - @param namespace(unicode): XEP namespace - @param callback(callable): method to call (must have the same signature as [fileSend]) - @param priority(int): pririoty of this method, the higher available will be used - @param method_name(unicode): short name for the method, namespace will be used if None + @param manager: object implementing canHandleFileSend, and fileSend methods + @param priority: pririoty of this manager, the higher available will be used """ - for data in self._file_callbacks: - if namespace == data[0]: - raise exceptions.ConflictError( - "A method with this namespace is already registered" - ) - self._file_callbacks.append( - (namespace, callback, priority, method_name or namespace) - ) - self._file_callbacks.sort(key=lambda data: data[2], reverse=True) + m_data = (manager, priority) + if m_data in self._file_managers: + raise exceptions.ConflictError( + f"Manager {manager} is already registered" + ) + if not hasattr(manager, "canHandleFileSend") or not hasattr(manager, "fileSend"): + raise ValueError( + f'{manager} must have both "canHandleFileSend" and "fileSend" methods to ' + 'be registered') + self._file_managers.append(m_data) + self._file_managers.sort(key=lambda m: m[1], reverse=True) - def unregister(self, namespace): - for idx, data in enumerate(self._file_callbacks): - if data[0] == namespace: - del [idx] - return - raise exceptions.NotFound("The namespace to unregister doesn't exist") + def unregister(self, manager): + for idx, data in enumerate(self._file_managers): + if data[0] == manager: + break + else: + raise exceptions.NotFound("The file manager {manager} is not registered") + del self._file_managers[idx] # Dialogs with user # the overwrite check is done here