summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml4
-rw-r--r--Makefile2
-rw-r--r--NEWS15
-rw-r--r--README2
-rw-r--r--paramiko/__init__.py2
-rw-r--r--paramiko/client.py8
-rw-r--r--paramiko/config.py2
-rw-r--r--paramiko/file.py4
-rw-r--r--paramiko/sftp_client.py128
-rw-r--r--setup.py2
11 files changed, 122 insertions, 48 deletions
diff --git a/.gitignore b/.gitignore
index 6f46f857..4b578950 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ dist/
.tox/
paramiko.egg-info/
test.log
+docs/
diff --git a/.travis.yml b/.travis.yml
index 90dbb80f..6896b897 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,9 +8,7 @@ install:
- pip install -e .
script: python test.py
notifications:
- email:
- on_failure: change
irc:
- channels: "irc.freenode.org#fabric"
+ channels: "irc.freenode.org#paramiko"
on_success: change
on_failure: change
diff --git a/Makefile b/Makefile
index 3c12990c..572f867a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
release: docs
python setup.py sdist register upload
-docs:
+docs: paramiko/*
epydoc --no-private -o docs/ paramiko
clean:
diff --git a/NEWS b/NEWS
index 55420463..1cfaa7e7 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,21 @@ Issues noted as "Fabric #NN" can be found at https://github.com/fabric/fabric/.
Releases
========
+v1.10.0 (DD MM YYYY)
+--------------------
+
+* #71: Add `SFTPClient.putfo` and `.getfo` methods to allow direct
+ uploading/downloading of file-like objects. Thanks to Eric Buehl for the
+ patch.
+* #113: Add `timeout` parameter to `SSHClient.exec_command` for easier setting
+ of the command's internal channel object's timeout. Thanks to Cernov Vladimir
+ for the patch.
+* #94: Remove duplication of SSH port constant. Thanks to Olle Lundberg for the
+ catch.
+* #80: Expose the internal "is closed" property of the file transfer class
+ `BufferedFile` as `.closed`, better conforming to Python's file interface.
+ Thanks to `@smunaut` and James Hiscock for catch & patch.
+
v1.9.0 (6th Nov 2012)
---------------------
diff --git a/README b/README
index 9e9cc3a7..c0c2fe03 100644
--- a/README
+++ b/README
@@ -5,7 +5,7 @@ paramiko
:Paramiko: Python SSH module
:Copyright: Copyright (c) 2003-2009 Robey Pointer <robeypointer@gmail.com>
-:Copyright: Copyright (c) 2012 Jeff Forcier <jeff@bitprophet.org>
+:Copyright: Copyright (c) 2013 Jeff Forcier <jeff@bitprophet.org>
:License: LGPL
:Homepage: https://github.com/paramiko/paramiko/
diff --git a/paramiko/__init__.py b/paramiko/__init__.py
index 29e470a6..7d7dcbf4 100644
--- a/paramiko/__init__.py
+++ b/paramiko/__init__.py
@@ -55,7 +55,7 @@ if sys.version_info < (2, 2):
__author__ = "Jeff Forcier <jeff@bitprophet.org>"
-__version__ = "1.9.0"
+__version__ = "1.10.0"
__license__ = "GNU Lesser General Public License (LGPL)"
diff --git a/paramiko/client.py b/paramiko/client.py
index 07560a39..a777b45b 100644
--- a/paramiko/client.py
+++ b/paramiko/client.py
@@ -28,6 +28,7 @@ import warnings
from paramiko.agent import Agent
from paramiko.common import *
+from paramiko.config import SSH_PORT
from paramiko.dsskey import DSSKey
from paramiko.hostkeys import HostKeys
from paramiko.resource import ResourceManager
@@ -37,8 +38,6 @@ from paramiko.transport import Transport
from paramiko.util import retry_on_signal
-SSH_PORT = 22
-
class MissingHostKeyPolicy (object):
"""
Interface for defining the policy that L{SSHClient} should use when the
@@ -350,7 +349,7 @@ class SSHClient (object):
self._agent.close()
self._agent = None
- def exec_command(self, command, bufsize=-1):
+ def exec_command(self, command, bufsize=-1, timeout=None):
"""
Execute a command on the SSH server. A new L{Channel} is opened and
the requested command is executed. The command's input and output
@@ -361,12 +360,15 @@ class SSHClient (object):
@type command: str
@param bufsize: interpreted the same way as by the built-in C{file()} function in python
@type bufsize: int
+ @param timeout: set command's channel timeout. See L{Channel.settimeout}.settimeout
+ @type timeout: int
@return: the stdin, stdout, and stderr of the executing command
@rtype: tuple(L{ChannelFile}, L{ChannelFile}, L{ChannelFile})
@raise SSHException: if the server fails to execute the command
"""
chan = self._transport.open_session()
+ chan.settimeout(timeout)
chan.exec_command(command)
stdin = chan.makefile('wb', bufsize)
stdout = chan.makefile('rb', bufsize)
diff --git a/paramiko/config.py b/paramiko/config.py
index 2828d903..d1ce9490 100644
--- a/paramiko/config.py
+++ b/paramiko/config.py
@@ -25,7 +25,7 @@ import os
import re
import socket
-SSH_PORT=22
+SSH_PORT = 22
proxy_re = re.compile(r"^(proxycommand)\s*=*\s*(.*)", re.I)
diff --git a/paramiko/file.py b/paramiko/file.py
index d4aec8e3..7e2904e1 100644
--- a/paramiko/file.py
+++ b/paramiko/file.py
@@ -354,6 +354,10 @@ class BufferedFile (object):
"""
return self
+ @property
+ def closed(self):
+ return self._closed
+
### overrides...
diff --git a/paramiko/sftp_client.py b/paramiko/sftp_client.py
index 3eaefc9c..8cb8ceaf 100644
--- a/paramiko/sftp_client.py
+++ b/paramiko/sftp_client.py
@@ -533,6 +533,56 @@ class SFTPClient (BaseSFTP):
"""
return self._cwd
+ def putfo(self, fl, remotepath, file_size=0, callback=None, confirm=True):
+ """
+ Copy the contents of an open file object (C{fl}) to the SFTP server as
+ C{remotepath}. Any exception raised by operations will be passed through.
+
+ The SFTP operations use pipelining for speed.
+
+ @param fl: opened file or file-like object to copy
+ @type localpath: object
+ @param remotepath: the destination path on the SFTP server
+ @type remotepath: str
+ @param file_size: optional size parameter passed to callback. If none is
+ specified, size defaults to 0
+ @type file_size: int
+ @param callback: optional callback function that accepts the bytes
+ transferred so far and the total bytes to be transferred
+ (since 1.7.4)
+ @type callback: function(int, int)
+ @param confirm: whether to do a stat() on the file afterwards to
+ confirm the file size (since 1.7.7)
+ @type confirm: bool
+
+ @return: an object containing attributes about the given file
+ (since 1.7.4)
+ @rtype: SFTPAttributes
+
+ @since: 1.4
+ """
+ fr = self.file(remotepath, 'wb')
+ fr.set_pipelined(True)
+ size = 0
+ try:
+ while True:
+ data = fl.read(32768)
+ fr.write(data)
+ size += len(data)
+ if callback is not None:
+ callback(size, file_size)
+ if len(data) == 0:
+ break
+ finally:
+ fr.close()
+ if confirm and file_size:
+ s = self.stat(remotepath)
+ if s.st_size != size:
+ raise IOError('size mismatch in put! %d != %d' % (s.st_size, size))
+ else:
+ s = SFTPAttributes()
+ return s
+
def put(self, localpath, remotepath, callback=None, confirm=True):
"""
Copy a local file (C{localpath}) to the SFTP server as C{remotepath}.
@@ -562,29 +612,46 @@ class SFTPClient (BaseSFTP):
file_size = os.stat(localpath).st_size
fl = file(localpath, 'rb')
try:
- fr = self.file(remotepath, 'wb')
- fr.set_pipelined(True)
- size = 0
- try:
- while True:
- data = fl.read(32768)
- if len(data) == 0:
- break
- fr.write(data)
- size += len(data)
- if callback is not None:
- callback(size, file_size)
- finally:
- fr.close()
+ return self.putfo(fl, remotepath, os.stat(localpath).st_size, callback, confirm)
finally:
fl.close()
- if confirm:
- s = self.stat(remotepath)
- if s.st_size != size:
- raise IOError('size mismatch in put! %d != %d' % (s.st_size, size))
- else:
- s = SFTPAttributes()
- return s
+
+ def getfo(self, remotepath, fl, callback=None):
+ """
+ Copy a remote file (C{remotepath}) from the SFTP server and write to
+ an open file or file-like object, C{fl}. Any exception raised by
+ operations will be passed through. This method is primarily provided
+ as a convenience.
+
+ @param remotepath: opened file or file-like object to copy to
+ @type remotepath: object
+ @param fl: the destination path on the local host or open file
+ object
+ @type localpath: str
+ @param callback: optional callback function that accepts the bytes
+ transferred so far and the total bytes to be transferred
+ (since 1.7.4)
+ @type callback: function(int, int)
+ @return: the number of bytes written to the opened file object
+
+ @since: 1.4
+ """
+ fr = self.file(remotepath, 'rb')
+ file_size = self.stat(remotepath).st_size
+ fr.prefetch()
+ try:
+ size = 0
+ while True:
+ data = fr.read(32768)
+ fl.write(data)
+ size += len(data)
+ if callback is not None:
+ callback(size, file_size)
+ if len(data) == 0:
+ break
+ finally:
+ fr.close()
+ return size
def get(self, remotepath, localpath, callback=None):
"""
@@ -603,25 +670,12 @@ class SFTPClient (BaseSFTP):
@since: 1.4
"""
- fr = self.file(remotepath, 'rb')
file_size = self.stat(remotepath).st_size
- fr.prefetch()
+ fl = file(localpath, 'wb')
try:
- fl = file(localpath, 'wb')
- try:
- size = 0
- while True:
- data = fr.read(32768)
- fl.write(data)
- size += len(data)
- if callback is not None:
- callback(size, file_size)
- if len(data) == 0:
- break
- finally:
- fl.close()
+ size = self.getfo(remotepath, fl, callback)
finally:
- fr.close()
+ fl.close()
s = os.stat(localpath)
if s.st_size != size:
raise IOError('size mismatch in get! %d != %d' % (s.st_size, size))
diff --git a/setup.py b/setup.py
index 1bb1a715..9812a4f4 100644
--- a/setup.py
+++ b/setup.py
@@ -52,7 +52,7 @@ if sys.platform == 'darwin':
setup(name = "paramiko",
- version = "1.9.0",
+ version = "1.10.0",
description = "SSH2 protocol library",
author = "Jeff Forcier",
author_email = "jeff@bitprophet.org",