diff options
-rw-r--r-- | .travis.yml | 7 | ||||
-rw-r--r-- | dev-requirements.txt | 4 | ||||
-rw-r--r-- | paramiko/_version.py | 2 | ||||
-rw-r--r-- | paramiko/buffered_pipe.py | 4 | ||||
-rw-r--r-- | paramiko/channel.py | 4 | ||||
-rw-r--r-- | paramiko/config.py | 4 | ||||
-rw-r--r-- | paramiko/pkey.py | 24 | ||||
-rw-r--r-- | paramiko/sftp_attr.py | 5 | ||||
-rw-r--r-- | paramiko/sftp_client.py | 2 | ||||
-rw-r--r-- | paramiko/util.py | 2 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rw-r--r-- | sites/www/changelog.rst | 12 | ||||
-rw-r--r-- | tasks.py | 17 | ||||
-rwxr-xr-x | test.py | 5 | ||||
-rwxr-xr-x | tests/test_sftp.py | 5 |
15 files changed, 63 insertions, 36 deletions
diff --git a/.travis.yml b/.travis.yml index 64f64e60..f841a71e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,9 @@ sudo: false python: - "2.6" - "2.7" - - "3.2" - "3.3" + - "3.4" + - "3.5" install: # Self-install for setup.py-driven deps - pip install -e . @@ -18,9 +19,7 @@ script: # Run 'docs' first since its objects.inv is referred to by 'www'. # Also force warnings to be errors since most of them tend to be actual # problems. - # Finally, skip them under Python 3.2 due to sphinx shenanigans - - "[[ $TRAVIS_PYTHON_VERSION != 3.2 ]] && invoke docs -o -W || true" - - "[[ $TRAVIS_PYTHON_VERSION != 3.2 ]] && invoke www -o -W || true" + - invoke docs -o -W www -o -W notifications: irc: channels: "irc.freenode.org#paramiko" diff --git a/dev-requirements.txt b/dev-requirements.txt index 059572cf..90cfd477 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,8 +1,8 @@ # Older junk tox>=1.4,<1.5 # For newer tasks like building Sphinx docs. -invoke>=0.10 -invocations>=0.9.2 +invoke>=0.11.1 +invocations>=0.11.0 sphinx>=1.1.3 alabaster>=0.6.1 releases>=0.5.2 diff --git a/paramiko/_version.py b/paramiko/_version.py index 0402fcf2..63bba727 100644 --- a/paramiko/_version.py +++ b/paramiko/_version.py @@ -1,2 +1,2 @@ -__version_info__ = (1, 13, 3) +__version_info__ = (1, 13, 4) __version__ = '.'.join(map(str, __version_info__)) diff --git a/paramiko/buffered_pipe.py b/paramiko/buffered_pipe.py index ac35b3e1..d5fe164e 100644 --- a/paramiko/buffered_pipe.py +++ b/paramiko/buffered_pipe.py @@ -81,7 +81,7 @@ class BufferedPipe (object): Feed new data into this pipe. This method is assumed to be called from a separate thread, so synchronization is done. - :param data: the data to add, as a `str` + :param data: the data to add, as a `str` or `bytes` """ self._lock.acquire() try: @@ -125,7 +125,7 @@ class BufferedPipe (object): :param int nbytes: maximum number of bytes to read :param float timeout: maximum seconds to wait (or ``None``, the default, to wait forever) - :return: the read data, as a `str` + :return: the read data, as a `bytes` :raises PipeTimeout: if a timeout was specified and no data was ready before that diff --git a/paramiko/channel.py b/paramiko/channel.py index 23323650..a36c70f4 100644 --- a/paramiko/channel.py +++ b/paramiko/channel.py @@ -574,8 +574,8 @@ class Channel (object): is returned, the channel stream has closed. :param int nbytes: maximum number of bytes to read. - :return: received data, as a `str` - + :return: received data, as a `bytes` + :raises socket.timeout: if no data is ready before the timeout set by `settimeout`. """ diff --git a/paramiko/config.py b/paramiko/config.py index c21de936..5c2efdcc 100644 --- a/paramiko/config.py +++ b/paramiko/config.py @@ -51,7 +51,7 @@ class SSHConfig (object): """ Read an OpenSSH config from the given file object. - :param file file_obj: a file-like object to read the config file from + :param file_obj: a file-like object to read the config file from """ host = {"host": ['*'], "config": {}} for line in file_obj: @@ -99,7 +99,7 @@ class SSHConfig (object): The host-matching rules of OpenSSH's ``ssh_config`` man page are used: For each parameter, the first obtained value will be used. The - configuration files contain sections separated by ``Host'' + configuration files contain sections separated by ``Host`` specifications, and that section is only applied for hosts that match one of the patterns given in the specification. diff --git a/paramiko/pkey.py b/paramiko/pkey.py index c8f84e0a..472c1287 100644 --- a/paramiko/pkey.py +++ b/paramiko/pkey.py @@ -171,8 +171,9 @@ class PKey (object): is useless on the abstract PKey class. :param str filename: name of the file to read - :param str password: an optional password to use to decrypt the key file, - if it's encrypted + :param str password: + an optional password to use to decrypt the key file, if it's + encrypted :return: a new `.PKey` based on the given private key :raises IOError: if there was an error reading the file @@ -187,18 +188,18 @@ class PKey (object): def from_private_key(cls, file_obj, password=None): """ Create a key object by reading a private key from a file (or file-like) - object. If the private key is encrypted and ``password`` is not ``None``, - the given password will be used to decrypt the key (otherwise + object. If the private key is encrypted and ``password`` is not + ``None``, the given password will be used to decrypt the key (otherwise `.PasswordRequiredException` is thrown). - :param file file_obj: the file to read from + :param file_obj: the file-like object to read from :param str password: an optional password to use to decrypt the key, if it's encrypted :return: a new `.PKey` based on the given private key :raises IOError: if there was an error reading the key - :raises PasswordRequiredException: if the private key file is encrypted, - and ``password`` is ``None`` + :raises PasswordRequiredException: + if the private key file is encrypted, and ``password`` is ``None`` :raises SSHException: if the key file is invalid """ key = cls(file_obj=file_obj, password=password) @@ -224,7 +225,7 @@ class PKey (object): Write private key contents into a file (or file-like) object. If the password is not ``None``, the key is encrypted before writing. - :param file file_obj: the file object to write into + :param file_obj: the file-like object to write into :param str password: an optional password to use to encrypt the key :raises IOError: if there was an error writing to the file @@ -274,7 +275,7 @@ class PKey (object): start += 1 # find end end = start - while (lines[end].strip() != '-----END ' + tag + ' PRIVATE KEY-----') and (end < len(lines)): + while end < len(lines) and lines[end].strip() != '-----END ' + tag + ' PRIVATE KEY-----': end += 1 # if we trudged to the end of the file, just try to cope. try: @@ -310,8 +311,9 @@ class PKey (object): a trivially-encoded format (base64) which is completely insecure. If a password is given, DES-EDE3-CBC is used. - :param str tag: ``"RSA"`` or ``"DSA"``, the tag used to mark the data block. - :param file filename: name of the file to write. + :param str tag: + ``"RSA"`` or ``"DSA"``, the tag used to mark the data block. + :param filename: name of the file to write. :param str data: data blob that makes up the private key. :param str password: an optional password to use to encrypt the file. diff --git a/paramiko/sftp_attr.py b/paramiko/sftp_attr.py index d12eff8d..708afc5a 100644 --- a/paramiko/sftp_attr.py +++ b/paramiko/sftp_attr.py @@ -210,12 +210,15 @@ class SFTPAttributes (object): # not all servers support uid/gid uid = self.st_uid gid = self.st_gid + size = self.st_size if uid is None: uid = 0 if gid is None: gid = 0 + if size is None: + size = 0 - return '%s 1 %-8d %-8d %8d %-12s %s' % (ks, uid, gid, self.st_size, datestr, filename) + return '%s 1 %-8d %-8d %8d %-12s %s' % (ks, uid, gid, size, datestr, filename) def asbytes(self): return b(str(self)) diff --git a/paramiko/sftp_client.py b/paramiko/sftp_client.py index 99a29e36..9a3e04b7 100644 --- a/paramiko/sftp_client.py +++ b/paramiko/sftp_client.py @@ -517,7 +517,7 @@ class SFTPClient(BaseSFTP): The SFTP operations use pipelining for speed. - :param file fl: opened file or file-like object to copy + :param fl: opened file or file-like object to copy :param str remotepath: the destination path on the SFTP server :param int file_size: optional size parameter passed to callback. If none is specified, diff --git a/paramiko/util.py b/paramiko/util.py index 46278a69..0d8ce32a 100644 --- a/paramiko/util.py +++ b/paramiko/util.py @@ -118,7 +118,7 @@ def safe_string(s): def bit_length(n): try: - return n.bitlength() + return n.bit_length() except AttributeError: norm = deflate_long(n, False) hbyte = byte_ord(norm[0]) @@ -86,6 +86,8 @@ setup( 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', ], **kw ) diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst index 9ce2eded..e435c65e 100644 --- a/sites/www/changelog.rst +++ b/sites/www/changelog.rst @@ -2,6 +2,18 @@ Changelog ========= +* :release:`1.13.4 <2015-11-02>` +* :bug:`366` Fix `~paramiko.sftp_attributes.SFTPAttributes` so its string + representation doesn't raise exceptions on empty/initialized instances. Patch + by Ulrich Petri. +* :bug:`359` Use correct attribute name when trying to use Python 3's + ``int.bit_length`` method; prior to fix, the Python 2 custom fallback + implementation was always used, even on Python 3. Thanks to Alex Gaynor. +* :support:`594 backported` Correct some post-Python3-port docstrings to + specify ``bytes`` type instead of ``str``. Credit to ``@redixin``. +* :bug:`565` Don't explode with ``IndexError`` when reading private key files + lacking an ``-----END <type> PRIVATE KEY-----`` footer. Patch courtesy of + Prasanna Santhanam. * :release:`1.13.3 <2014-12-19>` * :bug:`413` (also :issue:`414`, :issue:`420`, :issue:`454`) Be significantly smarter about polling & timing behavior when running proxy commands, to avoid @@ -3,14 +3,20 @@ from os.path import join from shutil import rmtree, copytree from invoke import Collection, ctask as task -from invocations.docs import docs, www +from invocations.docs import docs, www, sites from invocations.packaging import publish # Until we move to spec-based testing @task -def test(ctx): - ctx.run("python test.py --verbose", pty=True) +def test(ctx, coverage=False, flags=""): + if "--verbose" not in flags.split(): + flags += " --verbose" + runner = "python" + if coverage: + runner = "coverage run --source=paramiko" + ctx.run("{0} test.py {1}".format(runner, flags), pty=True) + @task def coverage(ctx): @@ -25,11 +31,12 @@ def release(ctx): # Move the built docs into where Epydocs used to live target = 'docs' rmtree(target, ignore_errors=True) - copytree(docs_build, target) + # TODO: make it easier to yank out this config val from the docs coll + copytree('sites/docs/_build', target) # Publish publish(ctx) # Remind print("\n\nDon't forget to update RTD's versions page for new minor releases!") -ns = Collection(test, coverage, release, docs, www) +ns = Collection(test, coverage, release, docs, www, sites) @@ -149,10 +149,7 @@ def main(): # TODO: make that not a problem, jeez for thread in threading.enumerate(): if thread is not threading.currentThread(): - if PY2: - thread._Thread__stop() - else: - thread._stop() + thread.join(timeout=1) # Exit correctly if not result.wasSuccessful(): sys.exit(1) diff --git a/tests/test_sftp.py b/tests/test_sftp.py index 2b6aa3b6..980e8367 100755 --- a/tests/test_sftp.py +++ b/tests/test_sftp.py @@ -776,6 +776,11 @@ class SFTPTest (unittest.TestCase): sftp.remove('%s/nonutf8data' % FOLDER) + def test_sftp_attributes_empty_str(self): + sftp_attributes = SFTPAttributes() + self.assertEqual(str(sftp_attributes), "?--------- 1 0 0 0 (unknown date) ?") + + if __name__ == '__main__': SFTPTest.init_loopback() # logging is required by test_N_file_with_percent |