Commits

Erik Svensson committed bd6de4f

Added more mutators to Torrent.
Added deprecation warnings.
Added deprecation messages.
Changed some documentation.

  • Participants
  • Parent commits 3a70d48

Comments (0)

Files changed (5)

File doc/_static/style.css

     border: 1px solid #dad503;
 }
 
+div.warning {
+    background: #ffe38b;
+    border: 1px solid #c2701a;
+}
+
+

File doc/reference/transmissionrpc.rst

 .. _RPC specification: http://trac.transmissionbt.com/wiki/rpc
 
 .. contents::
-    :depth: 3
+	:depth: 3
 
 Exceptions
 ==========
 
 .. autoclass:: HTTPHandlerError
 
-    .. attribute:: url
+	.. attribute:: url
 
-        The requested url.
+		The requested url.
 
-    .. attribute:: code
+	.. attribute:: code
 
-        HTTP error code.
+		HTTP error code.
 
-    .. attribute:: message
+	.. attribute:: message
 
-	    HTTP error message.
+		HTTP error message.
 
-    .. attribute:: headers
+	.. attribute:: headers
 
-	    HTTP headers.
+		HTTP headers.
 
-    .. attribute:: data
+	.. attribute:: data
 
-	    HTTP data.
+		HTTP data.
 
 Torrent object
 ==============
 
-Torrent is a class holding the information received from Transmission regarding
-a bittorrent transfer. All fetched torrent fields are accessible through this
-class using attributes. The attributes use underscore instead of hyphen in the
-names though. This class has a few convenience attributes using the torrent
-information.
+Torrent is a class holding the information received from Transmission regarding a bittorrent transfer.
+
+Attributes
+----------
+
+All fetched torrent fields are accessible through this class using attributes. The attributes use underscore instead of
+hyphen in the names though. This class has a few convenience attributes using the torrent information.
 
 Example:
 ::
 
 	>>> import transmissionrpc
-	>>> t = transmissionrpc.Torrent({'id': 1, 'comment': 'My torrent', 'addedDate': 1232281019})
+	>>> t = transmissionrpc.Torrent(None, {'id': 1, 'comment': 'My torrent', 'addedDate': 1232281019})
 	>>> t.comment
 	'My torrent'
 	>>> t.date_added
 	datetime.datetime(2009, 1, 18, 13, 16, 59)
 	>>>
 
+Mutators
+--------
+
+Some attributes can be changed, these are called mutators. These changes will be sent to the server when changed.
+To reload information from Transmission use ``update()``.
+
+Example:
+::
+
+	>>> import transmissionrpc
+	>>> c = transmissionrpc.Client()
+	>>> t = c.get_torrent(0)
+	>>> t.peer_limit
+	10
+	>>> t.peer_limit = 20
+	>>> t.update()
+	>>> t.peer_limit
+	20
+
+Reference
+---------
+
 .. autoclass:: Torrent
-    :members:
+	:members:
 
 Session object
 ==============
 
 Session is a class holding the session data for a Transmission session.
 
+Attributes
+----------
+
 Access the session field can be done through attributes.
 The attributes available are the same as the session arguments in the
 Transmission RPC specification, but with underscore instead of hyphen.
 ``download-dir`` -> ``download_dir``.
 
-.. class:: Session(fields = {})
+Reference
+---------
 
-	*fields* should be an dictionary build from session information from an
-	Transmission JSON-RPC result.
-
-.. method:: Session.update(other)
-
-	Updates the Session object with data from *other*.
-
-	*other* should be a Session object or session information from an
-	Transmission JSON-RPC result.
+.. autoclass:: Session
+	:members:
 
 Client object
 =============
 
-This is it. This class implements the JSON-RPC protocol to communicate with Transmission.
+This class implements the JSON-RPC protocol to communicate with Transmission.
 
 Torrent ids
 -----------
 a Client object or supply the named argument ``timeout`` in most methods of
 Client. The default timeout is 30 seconds.
 
+Reference
+---------
+
 .. autoclass:: Client
-    :members:
-
-
-
+	:members:

File test/torrent.py

     
     def testAttributes(self):
         torrent = transmissionrpc.Torrent(None, {'id': 42})
-        self.assertTrue(hasattr(torrent, 'client'))
         self.assertTrue(hasattr(torrent, 'id'))
-        self.assertEqual(torrent.client, None)
         self.assertEqual(torrent.id, 42)
         self.assertPropertyException(KeyError, torrent, 'status')
         self.assertPropertyException(KeyError, torrent, 'progress')
         self.assertEqual(torrent.date_done, datetime.datetime(2008,12,11,10,0,15))
         
         self.assertEqual(torrent.format_eta(), transmissionrpc.utils.format_timedelta(torrent.eta))
-        
-        torrent.fields['sizeWhenDone'] = 0
-        self.assertEqual(torrent.progress, 0)
 
     def testUnicode(self):
         torrent = transmissionrpc.Torrent(None, {'id': 42, 'name': 'あみ'})

File transmissionrpc/client.py

             LOGGER.warning('Using feature not supported by server. RPC version for server %d, feature introduced in %d.'
                 % (self.rpc_version, version))
 
-    def add(self, data, timeout=None, **kwargs):
+    def add_torrent(self, torrent, timeout=None, **kwargs):
         """
-        Add torrent to transfers list. Takes a base64 encoded .torrent file in data.
+        Add torrent to transfers list. Takes a uri to a torrent or base64 encoded torrent data.
         Additional arguments are:
 
         ===================== ===== =========== =============================================================
         ``priority_normal``   1 -               A list of file id's that should have normal priority.
         ===================== ===== =========== =============================================================
 
-        """
-        args = {}
-        if data:
-            args = {'metainfo': data}
-        elif 'metainfo' not in kwargs and 'filename' not in kwargs:
-            raise ValueError('No torrent data or torrent uri.')
-        for key, value in kwargs.iteritems():
-            argument = make_rpc_name(key)
-            (arg, val) = argument_value_convert('torrent-add', argument, value, self.rpc_version)
-            args[arg] = val
-        warnings.warn('add has been deprecated, please use add_torrent instead.', DeprecationWarning)
-        return self._request('torrent-add', args, timeout=timeout)
-
-    def add_uri(self, uri, **kwargs):
-        """
-        Add torrent to transfers list. Takes a uri to a torrent, supporting
-        all uri's supported by Transmissions torrent-add 'filename'
-        argument. Additional arguments are:
-
-        ===================== ===== =========== =============================================================
-        Argument              RPC   Replaced by Description
-        ===================== ===== =========== =============================================================
-        ``bandwidthPriority`` 8 -               Priority for this transfer.
-        ``cookies``           13 -              One or more HTTP cookie(s).
-        ``download_dir``      1 -               The directory where the downloaded contents will be saved in.
-        ``files_unwanted``    1 -               A list of file id's that shouldn't be downloaded.
-        ``files_wanted``      1 -               A list of file id's that should be downloaded.
-        ``paused``            1 -               If True, does not start the transfer when added.
-        ``peer_limit``        1 -               Maximum number of peers allowed.
-        ``priority_high``     1 -               A list of file id's that should have high priority.
-        ``priority_low``      1 -               A list of file id's that should have low priority.
-        ``priority_normal``   1 -               A list of file id's that should have normal priority.
-        ===================== ===== =========== =============================================================
-        """
-        if uri is None:
-            raise ValueError('add_uri requires a URI.')
-        # there has been some problem with T's built in torrent fetcher,
-        # use a python one instead
-        parsed_uri = urlparse.urlparse(uri)
-        torrent_data = None
-        if parsed_uri.scheme in ['file', 'ftp', 'ftps', 'http', 'https']:
-            torrent_file = urllib2.urlopen(uri)
-            torrent_data = base64.b64encode(torrent_file.read())
-        warnings.warn('add_uri has been deprecated, please use add_torrent instead.', DeprecationWarning)
-        if torrent_data:
-            return self.add(torrent_data, **kwargs)
-        else:
-            return self.add(None, filename=uri, **kwargs)
-
-    def remove(self, ids, delete_data=False, timeout=None):
-        """
-        remove torrent(s) with provided id(s). Local data is removed if
-        delete_data is True, otherwise not.
-        """
-        self._rpc_version_warning(3)
-        self._request('torrent-remove',
-                    {'delete-local-data':rpc_bool(delete_data)}, ids, True, timeout=timeout)
-    remove_torrent = remove
-
-    def start(self, ids, bypass_queue=False, timeout=None):
-        """start torrent(s) with provided id(s)"""
-        method = 'torrent-start'
-        if bypass_queue and self.rpc_version >= 14:
-            method = 'torrent-start-now'
-        self._request(method, {}, ids, True, timeout=timeout)
-    start_torrent = start
-
-    def start_all(self, bypass_queue=False, timeout=None):
-        """start all torrents respecting the queue order"""
-        torrent_list = self.list().values()
-        method = 'torrent-start'
-        if self.rpc_version >= 14:
-            if bypass_queue:
-                method = 'torrent-start-now'
-            torrent_list = sorted(torrent_list, key=operator.attrgetter('queuePosition'))
-        ids = [x.id for x in torrent_list]
-        self._request(method, {}, ids, True, timeout=timeout)
-
-    def stop(self, ids, timeout=None):
-        """stop torrent(s) with provided id(s)"""
-        self._request('torrent-stop', {}, ids, True, timeout=timeout)
-    stop_torrent = stop
-
-    def verify(self, ids, timeout=None):
-        """verify torrent(s) with provided id(s)"""
-        self._request('torrent-verify', {}, ids, True, timeout=timeout)
-    verify_torrent = verify
-
-    def reannounce(self, ids, timeout=None):
-        """Reannounce torrent(s) with provided id(s)"""
-        self._rpc_version_warning(5)
-        self._request('torrent-reannounce', {}, ids, True, timeout=timeout)
-    reannounce_torrent = reannounce
-
-    def add_torrent(self, torrent, timeout=None, **kwargs):
-        """
-        Add torrent to transfers list. Takes a uri to a torrent, supporting
-        all uri's supported by Transmissions torrent-add 'filename'
-        argument. Additional arguments are:
-
-        ===================== ===== =========== =============================================================
-        Argument              RPC   Replaced by Description
-        ===================== ===== =========== =============================================================
-        ``bandwidthPriority`` 8 -               Priority for this transfer.
-        ``cookies``           13 -              One or more HTTP cookie(s).
-        ``download_dir``      1 -               The directory where the downloaded contents will be saved in.
-        ``files_unwanted``    1 -               A list of file id's that shouldn't be downloaded.
-        ``files_wanted``      1 -               A list of file id's that should be downloaded.
-        ``paused``            1 -               If True, does not start the transfer when added.
-        ``peer_limit``        1 -               Maximum number of peers allowed.
-        ``priority_high``     1 -               A list of file id's that should have high priority.
-        ``priority_low``      1 -               A list of file id's that should have low priority.
-        ``priority_normal``   1 -               A list of file id's that should have normal priority.
-        ===================== ===== =========== =============================================================
+        Returns a Torrent object with limited fields.
         """
         if torrent is None:
-            raise ValueError('add_torrent requires a data or URI.')
+            raise ValueError('add_torrent requires data or a URI.')
         torrent_data = None
         try:
             # check if this is base64 data
             args[arg] = val
         return self._request('torrent-add', args, timeout=timeout).values()[0]
 
+    def add(self, data, timeout=None, **kwargs):
+        """
+
+        .. WARNING::
+            Deprecated, please use add_torrent.
+        """
+        args = {}
+        if data:
+            args = {'metainfo': data}
+        elif 'metainfo' not in kwargs and 'filename' not in kwargs:
+            raise ValueError('No torrent data or torrent uri.')
+        for key, value in kwargs.iteritems():
+            argument = make_rpc_name(key)
+            (arg, val) = argument_value_convert('torrent-add', argument, value, self.rpc_version)
+            args[arg] = val
+        warnings.warn('add has been deprecated, please use add_torrent instead.', DeprecationWarning)
+        return self._request('torrent-add', args, timeout=timeout)
+
+    def add_uri(self, uri, **kwargs):
+        """
+
+        .. WARNING::
+            Deprecated, please use add_torrent.
+        """
+        if uri is None:
+            raise ValueError('add_uri requires a URI.')
+        # there has been some problem with T's built in torrent fetcher,
+        # use a python one instead
+        parsed_uri = urlparse.urlparse(uri)
+        torrent_data = None
+        if parsed_uri.scheme in ['file', 'ftp', 'ftps', 'http', 'https']:
+            torrent_file = urllib2.urlopen(uri)
+            torrent_data = base64.b64encode(torrent_file.read())
+        warnings.warn('add_uri has been deprecated, please use add_torrent instead.', DeprecationWarning)
+        if torrent_data:
+            return self.add(torrent_data, **kwargs)
+        else:
+            return self.add(None, filename=uri, **kwargs)
+
+    def remove_torrent(self, ids, delete_data=False, timeout=None):
+        """
+        remove torrent(s) with provided id(s). Local data is removed if
+        delete_data is True, otherwise not.
+        """
+        self._rpc_version_warning(3)
+        self._request('torrent-remove',
+                    {'delete-local-data':rpc_bool(delete_data)}, ids, True, timeout=timeout)
+
+    def remove(self, ids, delete_data=False, timeout=None):
+        """
+
+        .. WARNING::
+            Deprecated, please use remove_torrent.
+        """
+        warnings.warn('remove has been deprecated, please use remove_torrent instead.', DeprecationWarning)
+        self.remove_torrent(ids, delete_data, timeout)
+
+    def start_torrent(self, ids, bypass_queue=False, timeout=None):
+        """Start torrent(s) with provided id(s)"""
+        method = 'torrent-start'
+        if bypass_queue and self.rpc_version >= 14:
+            method = 'torrent-start-now'
+        self._request(method, {}, ids, True, timeout=timeout)
+
+    def start(self, ids, bypass_queue=False, timeout=None):
+        """
+
+        .. WARNING::
+            Deprecated, please use start_torrent.
+        """
+        warnings.warn('start has been deprecated, please use start_torrent instead.', DeprecationWarning)
+        self.start_torrent(ids, bypass_queue, timeout)
+
+    def start_all(self, bypass_queue=False, timeout=None):
+        """Start all torrents respecting the queue order"""
+        torrent_list = self.get_torrents()
+        method = 'torrent-start'
+        if self.rpc_version >= 14:
+            if bypass_queue:
+                method = 'torrent-start-now'
+            torrent_list = sorted(torrent_list, key=operator.attrgetter('queuePosition'))
+        ids = [x.id for x in torrent_list]
+        self._request(method, {}, ids, True, timeout=timeout)
+
+    def stop_torrent(self, ids, timeout=None):
+        """stop torrent(s) with provided id(s)"""
+        self._request('torrent-stop', {}, ids, True, timeout=timeout)
+
+    def stop(self, ids, timeout=None):
+        """
+
+        .. WARNING::
+            Deprecated, please use stop_torrent.
+        """
+        warnings.warn('stop has been deprecated, please use stop_torrent instead.', DeprecationWarning)
+        self.stop_torrent(ids, timeout)
+
+    def verify_torrent(self, ids, timeout=None):
+        """verify torrent(s) with provided id(s)"""
+        self._request('torrent-verify', {}, ids, True, timeout=timeout)
+
+    def verify(self, ids, timeout=None):
+        """
+
+        .. WARNING::
+            Deprecated, please use verify_torrent.
+        """
+        warnings.warn('verify has been deprecated, please use verify_torrent instead.', DeprecationWarning)
+        self.verify_torrent(ids, timeout)
+
+    def reannounce_torrent(self, ids, timeout=None):
+        """Reannounce torrent(s) with provided id(s)"""
+        self._rpc_version_warning(5)
+        self._request('torrent-reannounce', {}, ids, True, timeout=timeout)
+
+    def reannounce(self, ids, timeout=None):
+        """
+
+        .. WARNING::
+            Deprecated, please use reannounce_torrent.
+        """
+        warnings.warn('reannounce has been deprecated, please use reannounce_torrent instead.', DeprecationWarning)
+        self.reannounce_torrent(ids, timeout)
+
     def get_torrent(self, id, arguments=None, timeout=None):
-        """Get information for torrent with provided id."""
+        """
+        Get information for torrent with provided id.
+
+        Returns a Torrent object.
+        """
         if not arguments:
             arguments = self.torrent_get_arguments
         if not isinstance(id, (int, long, str, unicode)):
         return self._request('torrent-get', {'fields': arguments}, id, require_ids=True, timeout=timeout)[id]
 
     def get_torrents(self, ids=None, arguments=None, timeout=None):
-        """Get information for torrents with provided ids."""
+        """
+        Get information for torrents with provided ids.
+
+        Returns a list of Torrent object.
+        """
         if not arguments:
             arguments = self.torrent_get_arguments
         return self._request('torrent-get', {'fields': arguments}, ids, timeout=timeout).values()
 
     def info(self, ids=None, arguments=None, timeout=None):
-        """Get detailed information for torrent(s) with provided id(s)."""
+        """
+
+        .. WARNING::
+            Deprecated, please use get_torrent or get_torrents. Please note that the return argument has changed in
+            the new methods. info returns a dictionary indexed by torrent id.
+        """
         warnings.warn('info has been deprecated, please use get_torrent or get_torrents instead.', DeprecationWarning)
         if not arguments:
             arguments = self.torrent_get_arguments
         return self._request('torrent-get', {'fields': arguments}, ids, timeout=timeout)
 
+    def list(self, timeout=None):
+        """
+
+        .. WARNING::
+            Deprecated, please use get_torrent or get_torrents. Please note that the return argument has changed in
+            the new methods. list returns a dictionary indexed by torrent id.
+        """
+        warnings.warn('list has been deprecated, please use get_torrent or get_torrents instead.', DeprecationWarning)
+        fields = ['id', 'hashString', 'name', 'sizeWhenDone', 'leftUntilDone'
+            , 'eta', 'status', 'rateUpload', 'rateDownload', 'uploadedEver'
+            , 'downloadedEver', 'uploadRatio', 'queuePosition']
+        return self._request('torrent-get', {'fields': fields}, timeout=timeout)
+
     def get_files(self, ids=None, timeout=None):
         """
     	Get list of files for provided torrent id(s). If ids is empty,
                 args['priority_low'] = low
             self.change([tid], **args)
 
-    def list(self, timeout=None):
-        """list all torrents"""
-        warnings.warn('list has been deprecated, please use get_torrent or get_torrents instead.', DeprecationWarning)
-        fields = ['id', 'hashString', 'name', 'sizeWhenDone', 'leftUntilDone'
-            , 'eta', 'status', 'rateUpload', 'rateDownload', 'uploadedEver'
-            , 'downloadedEver', 'uploadRatio', 'queuePosition']
-        return self._request('torrent-get', {'fields': fields}, timeout=timeout)
-
-    def change(self, ids, timeout=None, **kwargs):
+    def change_torrent(self, ids, timeout=None, **kwargs):
         """
     	Change torrent parameters for the torrent(s) with the supplied id's. The
     	parameters are:
             self._request('torrent-set', args, ids, True, timeout=timeout)
         else:
             ValueError("No arguments to set")
-    change_torrent = change
 
-    def move(self, ids, location, timeout=None):
+    def change(self, ids, timeout=None, **kwargs):
+        """
+
+        .. WARNING::
+            Deprecated, please use change_torrent.
+        """
+        warnings.warn('change has been deprecated, please use change_torrent instead.', DeprecationWarning)
+        self.change_torrent(ids, timeout, **kwargs)
+
+    def move_torrent_data(self, ids, location, timeout=None):
         """Move torrent data to the new location."""
         self._rpc_version_warning(6)
         args = {'location': location, 'move': True}
         self._request('torrent-set-location', args, ids, True, timeout=timeout)
-    move_torrent_data = move
 
-    def locate(self, ids, location, timeout=None):
-        """Locate torrent data at the location."""
+    def move(self, ids, location, timeout=None):
+        """
+
+        .. WARNING::
+            Deprecated, please use move_torrent_data.
+        """
+        warnings.warn('move has been deprecated, please use move_torrent_data instead.', DeprecationWarning)
+        self.move_torrent_data(ids, location, timeout)
+
+    def locate_torrent_data(self, ids, location, timeout=None):
+        """Locate torrent data at the provided location."""
         self._rpc_version_warning(6)
         args = {'location': location, 'move': False}
         self._request('torrent-set-location', args, ids, True, timeout=timeout)
-    locate_torrent_data = locate
+
+    def locate(self, ids, location, timeout=None):
+        """
+
+        .. WARNING::
+            Deprecated, please use locate_torrent_data.
+        """
+        warnings.warn('locate has been deprecated, please use locate_torrent_data instead.', DeprecationWarning)
+        self.locate_torrent_data(ids, location, timeout)
 
     def queue_top(self, ids, timeout=None):
         """Move transfer to the top of the queue."""

File transmissionrpc/torrent.py

 import sys, datetime
 from collections import namedtuple
 
-from transmissionrpc.constants import PRIORITY
+from transmissionrpc.constants import PRIORITY, RATIO_LIMIT, IDLE_LIMIT
 from transmissionrpc.utils import format_timedelta
 
 Field = namedtuple('Field', ['value', 'dirty'])
         else:
             return format_timedelta(self.eta)
 
-    def _get_priority(self):
-        """
-        Get the priority as string.
-        Can be one of 'low', 'normal', 'high'.
-        """
-        return PRIORITY[self._fields['bandwidthPriority'].value]
-
-    def _set_priority(self, priority):
-        """
-        Set the priority as string.
-        Can be one of 'low', 'normal', 'high'.
-        """
-        if isinstance(priority, (str, unicode)):
-            self._fields['bandwidthPriority'] = Field(PRIORITY[priority], True)
-            self._push()
-
-    priority = property(_get_priority, _set_priority, None
-        , "Priority as string. Can be one of 'low', 'normal', 'high'.")
-
-    def _get_upload_limit(self):
-        """
-        Get the upload limit.
-        Can be a number or None.
-        """
-        if self._fields['uploadLimited'].value:
-            return self._fields['uploadLimit'].value
-        else:
-            return None
-
-    def _set_upload_limit(self, limit):
-        """
-        Get the upload limit.
-        Can be a number, 'session' or None.
-        """
-        if isinstance(limit, (int, long)):
-            self._fields['uploadLimited'] = Field(True, True)
-            self._fields['uploadLimit'] = Field(limit, True)
-            self._push()
-        elif limit == None:
-            self._fields['uploadLimited'] = Field(False, True)
-            self._push()
-        else:
-            raise ValueError("Not a valid limit")
-
-    upload_limit = property(_get_upload_limit, _set_upload_limit, None, "Upload limit in Kbps or None")
-
     def _get_download_limit(self):
         """
         Get the download limit.
         else:
             raise ValueError("Not a valid limit")
 
-    download_limit = property(_get_download_limit, _set_download_limit, None, "Download limit in Kbps or None")
+    download_limit = property(_get_download_limit, _set_download_limit, None, "Download limit in Kbps or None. This is a mutator.")
+
+    def _get_peer_limit(self):
+        """
+        Get the peer limit.
+        """
+        return self._fields['peer_limit'].value
+
+    def _set_peer_limit(self, limit):
+        """
+        Set the peer limit.
+        """
+        if isinstance(limit, (int, long)):
+            self._fields['peer_limit'] = Field(limit, True)
+            self._push()
+        else:
+            raise ValueError("Not a valid limit")
+
+    peer_limit = property(_get_peer_limit, _set_peer_limit, None, "Peer limit. This is a mutator.")
+
+    def _get_priority(self):
+        """
+        Get the priority as string.
+        Can be one of 'low', 'normal', 'high'.
+        """
+        return PRIORITY[self._fields['bandwidthPriority'].value]
+
+    def _set_priority(self, priority):
+        """
+        Set the priority as string.
+        Can be one of 'low', 'normal', 'high'.
+        """
+        if isinstance(priority, (str, unicode)):
+            self._fields['bandwidthPriority'] = Field(PRIORITY[priority], True)
+            self._push()
+
+    priority = property(_get_priority, _set_priority, None
+        , "Bandwidth priority as string. Can be one of 'low', 'normal', 'high'. This is a mutator.")
+
+    def _get_seed_idle_limit(self):
+        """
+        Get the seed idle limit in minutes.
+        """
+        return self._fields['seedIdleLimit'].value
+
+    def _set_seed_idle_limit(self, limit):
+        """
+        Set the seed idle limit in minutes.
+        """
+        if isinstance(limit, (int, long)):
+            self._fields['seedIdleLimit'] = Field(limit, True)
+            self._push()
+        else:
+            raise ValueError("Not a valid limit")
+
+    seed_idle_limit = property(_get_seed_idle_limit, _set_seed_idle_limit, None
+        , "Torrent seed idle limit in minutes. Also see seed_idle_mode. This is a mutator.")
+
+    def _get_seed_idle_mode(self):
+        """
+        Get the seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'.
+        """
+        return IDLE_LIMIT[self._fields['seedIdleMode'].value]
+
+    def _set_seed_idle_mode(self, mode):
+        """
+        Set the seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'.
+        """
+        if isinstance(mode, str):
+            self._fields['seedIdleMode'] = Field(IDLE_LIMIT[mode], True)
+            self._push()
+        else:
+            raise ValueError("Not a valid limit")
+
+    seed_idle_mode = property(_get_seed_idle_mode, _set_seed_idle_mode, None,
+        """
+        Seed idle mode as string. Can be one of 'global', 'single' or 'unlimited'.
+
+         * global, use session seed idle limit.
+         * single, use torrent seed idle limit. See seed_idle_limit.
+         * unlimited, no seed idle limit.
+
+        This is a mutator.
+        """
+    )
+
+    def _get_seed_ratio_limit(self):
+        """
+        Get the seed ratio limit as float.
+        """
+        return float(self._fields['seedRatioLimit'].value)
+
+    def _set_seed_ratio_limit(self, limit):
+        """
+        Set the seed ratio limit as float.
+        """
+        if isinstance(limit, (int, long, float)) and limit >= 0.0:
+            self._fields['seedRatioLimit'] = Field(float(limit), True)
+            self._push()
+        else:
+            raise ValueError("Not a valid limit")
+
+    seed_ratio_limit = property(_get_seed_ratio_limit, _set_seed_ratio_limit, None
+        , "Torrent seed ratio limit as float. Also see seed_ratio_mode. This is a mutator.")
+
+    def _get_seed_ratio_mode(self):
+        """
+        Get the seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'.
+        """
+        return RATIO_LIMIT[self._fields['seedRatioMode'].value]
+
+    def _set_seed_ratio_mode(self, mode):
+        """
+        Set the seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'.
+        """
+        if isinstance(mode, str):
+            self._fields['seedRatioMode'] = Field(RATIO_LIMIT[mode], True)
+            self._push()
+        else:
+            raise ValueError("Not a valid limit")
+
+    seed_ratio_mode = property(_get_seed_ratio_mode, _set_seed_ratio_mode, None,
+        """
+        Seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'.
+
+         * global, use session seed ratio limit.
+         * single, use torrent seed ratio limit. See seed_ratio_limit.
+         * unlimited, no seed ratio limit.
+
+        This is a mutator.
+        """
+    )
+
+    def _get_upload_limit(self):
+        """
+        Get the upload limit.
+        Can be a number or None.
+        """
+        if self._fields['uploadLimited'].value:
+            return self._fields['uploadLimit'].value
+        else:
+            return None
+
+    def _set_upload_limit(self, limit):
+        """
+        Set the upload limit.
+        Can be a number, 'session' or None.
+        """
+        if isinstance(limit, (int, long)):
+            self._fields['uploadLimited'] = Field(True, True)
+            self._fields['uploadLimit'] = Field(limit, True)
+            self._push()
+        elif limit == None:
+            self._fields['uploadLimited'] = Field(False, True)
+            self._push()
+        else:
+            raise ValueError("Not a valid limit")
+
+    upload_limit = property(_get_upload_limit, _set_upload_limit, None, "Upload limit in Kbps or None. This is a mutator.")
 
     def _get_queue_position(self):
         if self._rpc_version() >= 14:
     queue_position = property(_get_queue_position, _set_queue_position, None, "Queue position")
 
     def _dirty_fields(self):
-        outgoing_keys = ['bandwidthPriority', 'downloadLimit', 'downloadLimited', 'queuePosition'
-            , 'uploadLimit', 'uploadLimited']
+        """Enumerate changed fields"""
+        outgoing_keys = ['bandwidthPriority', 'downloadLimit', 'downloadLimited', 'peer_limit', 'queuePosition'
+            , 'seedIdleLimit', 'seedIdleMode', 'seedRatioLimit', 'seedRatioMode', 'uploadLimit', 'uploadLimited']
         fields = []
         for key in outgoing_keys:
             if key in self._fields and self._fields[key].dirty:
         return fields
 
     def _push(self):
+        """Push changed fields to the server"""
         dirty = self._dirty_fields()
         args = {}
         for key in dirty:
             self._client.change_torrent(self.id, **args)
 
     def update(self, timeout=None):
+        """Update the torrent information."""
         self._push()
         torrent = self._client.get_torrent(self.id, timeout=timeout)
         self._update_fields(torrent)
 
     def start(self, bypass_queue=False, timeout=None):
-        """Move torrent data to location"""
+        """
+        Start the torrent.
+        """
         self._incoming_pending = True
         self._client.start_torrent(self.id, bypass_queue=bypass_queue, timeout=timeout)
 
     def stop(self, timeout=None):
-        """Move torrent data to location"""
+        """Stop the torrent."""
         self._incoming_pending = True
         self._client.stop_torrent(self.id, timeout=timeout)
 
     def move_data(self, location, timeout=None):
-        """Move torrent data to location"""
+        """Move torrent data to location."""
         self._incoming_pending = True
         self._client.move_torrent_data(self.id, location, timeout=timeout)
 
     def locate_data(self, location, timeout=None):
-        """Locate torrent data at location"""
+        """Locate torrent data at location."""
         self._incoming_pending = True
-        self._client.locate_torrent_data(self.id, location, timeout=timeout)
+        self._client.locate_torrent_data(self.id, location, timeout=timeout)