Source code for multibase

"""
.. _multibase-class:

MultiBase
=========

MultiBase is a class that can be inherited to easily create a class that can
send multiple XMLRPC commands in one request, using the xmlrpclib.MultiCall
class. RTorrentQuery and TorrentQuery implement this class.

It also overrides several functions like __getattr__ and __call__ to
make usage simple:

.. code-block:: python

    t = Torrent.query() # Returns a TorrentQuery object which inherits from
                        # MultiBase
    t.get_name().get_hash()
    t.get_upload_rate()

    print t.all()
"""

import xmlrpclib
import socket

from lib.xmlrpc import RTorrentXMLRPC

[docs]class MultiBase(object): """ """ def __init__(self, target, *args): """ """ self.s = RTorrentXMLRPC(target) self.m = xmlrpclib.MultiCall(self.s) # Stack to put commands on self._groups = [[]] self._group_args = args def __call__(self, attr, *args): """ Add the attribute ``attr'' to the list we want to fetch. Return self so we can chain calls. """ _attr = self._convert_command(attr) total_args = list(self._group_args) total_args.extend(args) getattr(self.m, _attr)(*total_args) self._groups[-1].append(attr) return self def __getattr__(self, attr): """ Used to add commands. """ return lambda *args: self(attr, *args) # calls __call__
[docs] def new_group(self, *args): """ Use this to create a new group. You can chain calls as well. """ self._groups.append([]) self._group_args = args return self
[docs] def all(self, _type=None): """ Returns a list of the results. _type can be 'list' or AttributeDictMultiResult. """ if _type is None: _type = AttributeDictMultiResult if _type not in (AttributeDictMultiResult, list): raise InvalidTorrentCommandException('Invalid _type: %s' % str(_type)) try: xmlres = list(self.m()) except xmlrpclib.Fault, e: raise InvalidTorrentException(e) except socket.error, s: raise InvalidConnectionException(s) if _type is list: self._flush() return xmlres xmlres.reverse() result = [] for group in self._groups: res = [] for command in group: res.append(xmlres.pop()) result.append(AttributeDictMultiResult(zip(group, res))) self._flush() return result
[docs] def first(self, _type=None): """ Return the first result. """ res = self.all(_type) if len(res): return res[0] else: return None
def _flush(self): pass def _convert_command(self, command): """ Convert command based on self._rpc_methods to rtorrent command. """ if command in self._rpc_methods: return self._rpc_methods[command][0] else: raise InvalidTorrentCommandException("%s is not a valid command" % command)
[docs]class InvalidTorrentCommandException(Exception): """ Thrown on an invalid command. """
[docs]class InvalidTorrentException(Exception): """ Thrown on xmlrpc error. """
[docs]class InvalidConnectionException(Exception): """ Thrown on xmlrpc error. """
[docs]class AttributeDictMultiResult(dict): """ AttributeDictMultiResult is used by MultiBase .all() calls to return data in a somewhat usable manner. It's basically a dict with an extra feature to access the dict values via .<name> instead of [name]. """ def __getattr__(self, attr): if attr in self: return self[attr] else: raise AttributeError('%s not in dict' % attr)