[Python-modules-commits] [python-scp] 01/04: Imported Upstream version 0.10.2
Sandro Tosi
morph at moszumanska.debian.org
Tue Jun 30 14:32:51 UTC 2015
This is an automated email from the git hooks/post-receive script.
morph pushed a commit to branch bpo80
in repository python-scp.
commit a78d2699afb79b6d6e62249c67b173d0f15b9982
Author: Sandro Tosi <morph at debian.org>
Date: Tue Jun 30 09:49:04 2015 -0400
Imported Upstream version 0.10.2
---
LICENSE.txt | 14 ++
MANIFEST.in | 2 +
PKG-INFO | 67 ++++++
README.rst | 57 +++++
scp.egg-info/PKG-INFO | 67 ++++++
scp.egg-info/SOURCES.txt | 10 +
scp.egg-info/dependency_links.txt | 1 +
scp.egg-info/requires.txt | 1 +
scp.egg-info/top_level.txt | 1 +
scp.py | 470 ++++++++++++++++++++++++++++++++++++++
setup.cfg | 5 +
setup.py | 22 ++
12 files changed, 717 insertions(+)
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..7da26b9
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,14 @@
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..97e2ad3
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,2 @@
+include LICENSE.txt
+include README.rst
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..953685d
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,67 @@
+Metadata-Version: 1.0
+Name: scp
+Version: 0.10.2
+Summary: scp module for paramiko
+Home-page: https://github.com/jbardin/scp.py
+Author: James Bardin
+Author-email: j.bardin at gmail.com
+License: LGPL
+Description: Pure python scp module
+ ======================
+
+ The scp.py module uses a paramiko transport to send and recieve files via the
+ scp1 protocol. This is the protocol as referenced from the openssh scp program,
+ and has only been tested with this implementation.
+
+
+ Example
+ -------
+
+ .. code-block:: python
+
+ from paramiko import SSHClient
+ from scp import SCPClient
+
+ ssh = SSHClient()
+ ssh.load_system_host_keys()
+ ssh.connect('example.com')
+
+ # SCPCLient takes a paramiko transport as its only argument
+ scp = SCPClient(ssh.get_transport())
+
+ scp.put('test.txt', 'test2.txt')
+ scp.get('test2.txt')
+
+ scp.close()
+
+
+ .. code-block::
+
+ $ md5sum test.txt test2.txt
+ fc264c65fb17b7db5237cf7ce1780769 test.txt
+ fc264c65fb17b7db5237cf7ce1780769 test2.txt
+
+ Using 'with' keyword
+ ------------------
+
+ .. code-block:: python
+
+ from paramiko import SSHClient
+ from scp import SCPClient
+
+ ssh = SSHClient()
+ ssh.load_system_host_keys()
+ ssh.connect('example.com')
+
+ with SCPClient(ssh.get_transport()) as scp:
+ scp.put('test.txt', 'test2.txt')
+ scp.get('test2.txt')
+
+
+ .. code-block::
+
+ $ md5sum test.txt test2.txt
+ fc264c65fb17b7db5237cf7ce1780769 test.txt
+ fc264c65fb17b7db5237cf7ce1780769 test2.txt
+
+Platform: UNKNOWN
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..7741e0d
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,57 @@
+Pure python scp module
+======================
+
+The scp.py module uses a paramiko transport to send and recieve files via the
+scp1 protocol. This is the protocol as referenced from the openssh scp program,
+and has only been tested with this implementation.
+
+
+Example
+-------
+
+.. code-block:: python
+
+ from paramiko import SSHClient
+ from scp import SCPClient
+
+ ssh = SSHClient()
+ ssh.load_system_host_keys()
+ ssh.connect('example.com')
+
+ # SCPCLient takes a paramiko transport as its only argument
+ scp = SCPClient(ssh.get_transport())
+
+ scp.put('test.txt', 'test2.txt')
+ scp.get('test2.txt')
+
+ scp.close()
+
+
+.. code-block::
+
+ $ md5sum test.txt test2.txt
+ fc264c65fb17b7db5237cf7ce1780769 test.txt
+ fc264c65fb17b7db5237cf7ce1780769 test2.txt
+
+Using 'with' keyword
+------------------
+
+.. code-block:: python
+
+ from paramiko import SSHClient
+ from scp import SCPClient
+
+ ssh = SSHClient()
+ ssh.load_system_host_keys()
+ ssh.connect('example.com')
+
+ with SCPClient(ssh.get_transport()) as scp:
+ scp.put('test.txt', 'test2.txt')
+ scp.get('test2.txt')
+
+
+.. code-block::
+
+ $ md5sum test.txt test2.txt
+ fc264c65fb17b7db5237cf7ce1780769 test.txt
+ fc264c65fb17b7db5237cf7ce1780769 test2.txt
diff --git a/scp.egg-info/PKG-INFO b/scp.egg-info/PKG-INFO
new file mode 100644
index 0000000..953685d
--- /dev/null
+++ b/scp.egg-info/PKG-INFO
@@ -0,0 +1,67 @@
+Metadata-Version: 1.0
+Name: scp
+Version: 0.10.2
+Summary: scp module for paramiko
+Home-page: https://github.com/jbardin/scp.py
+Author: James Bardin
+Author-email: j.bardin at gmail.com
+License: LGPL
+Description: Pure python scp module
+ ======================
+
+ The scp.py module uses a paramiko transport to send and recieve files via the
+ scp1 protocol. This is the protocol as referenced from the openssh scp program,
+ and has only been tested with this implementation.
+
+
+ Example
+ -------
+
+ .. code-block:: python
+
+ from paramiko import SSHClient
+ from scp import SCPClient
+
+ ssh = SSHClient()
+ ssh.load_system_host_keys()
+ ssh.connect('example.com')
+
+ # SCPCLient takes a paramiko transport as its only argument
+ scp = SCPClient(ssh.get_transport())
+
+ scp.put('test.txt', 'test2.txt')
+ scp.get('test2.txt')
+
+ scp.close()
+
+
+ .. code-block::
+
+ $ md5sum test.txt test2.txt
+ fc264c65fb17b7db5237cf7ce1780769 test.txt
+ fc264c65fb17b7db5237cf7ce1780769 test2.txt
+
+ Using 'with' keyword
+ ------------------
+
+ .. code-block:: python
+
+ from paramiko import SSHClient
+ from scp import SCPClient
+
+ ssh = SSHClient()
+ ssh.load_system_host_keys()
+ ssh.connect('example.com')
+
+ with SCPClient(ssh.get_transport()) as scp:
+ scp.put('test.txt', 'test2.txt')
+ scp.get('test2.txt')
+
+
+ .. code-block::
+
+ $ md5sum test.txt test2.txt
+ fc264c65fb17b7db5237cf7ce1780769 test.txt
+ fc264c65fb17b7db5237cf7ce1780769 test2.txt
+
+Platform: UNKNOWN
diff --git a/scp.egg-info/SOURCES.txt b/scp.egg-info/SOURCES.txt
new file mode 100644
index 0000000..9effc40
--- /dev/null
+++ b/scp.egg-info/SOURCES.txt
@@ -0,0 +1,10 @@
+LICENSE.txt
+MANIFEST.in
+README.rst
+scp.py
+setup.py
+scp.egg-info/PKG-INFO
+scp.egg-info/SOURCES.txt
+scp.egg-info/dependency_links.txt
+scp.egg-info/requires.txt
+scp.egg-info/top_level.txt
\ No newline at end of file
diff --git a/scp.egg-info/dependency_links.txt b/scp.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/scp.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/scp.egg-info/requires.txt b/scp.egg-info/requires.txt
new file mode 100644
index 0000000..8608c1b
--- /dev/null
+++ b/scp.egg-info/requires.txt
@@ -0,0 +1 @@
+paramiko
diff --git a/scp.egg-info/top_level.txt b/scp.egg-info/top_level.txt
new file mode 100644
index 0000000..d5c76b3
--- /dev/null
+++ b/scp.egg-info/top_level.txt
@@ -0,0 +1 @@
+scp
diff --git a/scp.py b/scp.py
new file mode 100644
index 0000000..c4f2499
--- /dev/null
+++ b/scp.py
@@ -0,0 +1,470 @@
+# scp.py
+# Copyright (C) 2008 James Bardin <j.bardin at gmail.com>
+
+"""
+Utilities for sending files over ssh using the scp1 protocol.
+"""
+
+__version__ = '0.10.2'
+
+import locale
+import os
+import re
+from socket import timeout as SocketTimeout
+
+
+# this is quote from the shlex module, added in py3.3
+_find_unsafe = re.compile(br'[^\w@%+=:,./~-]').search
+
+
+def _sh_quote(s):
+ """Return a shell-escaped version of the string `s`."""
+ if not s:
+ return b""
+ if _find_unsafe(s) is None:
+ return s
+
+ # use single quotes, and put single quotes into double quotes
+ # the string $'b is then quoted as '$'"'"'b'
+ return b"'" + s.replace(b"'", b"'\"'\"'") + b"'"
+
+
+# Unicode conversion functions; assume UTF-8
+
+def asbytes(s):
+ """Turns unicode into bytes, if needed.
+
+ Assumes UTF-8.
+ """
+ if isinstance(s, bytes):
+ return s
+ else:
+ return s.encode('utf-8')
+
+
+def asunicode(s):
+ """Turns bytes into unicode, if needed.
+
+ Uses UTF-8.
+ """
+ if isinstance(s, bytes):
+ return s.decode('utf-8', 'replace')
+ else:
+ return s
+
+
+# os.path.sep is unicode on Python 3, no matter the platform
+bytes_sep = asbytes(os.path.sep)
+
+
+# Unicode conversion function for Windows
+# Used to convert local paths if the local machine is Windows
+
+def asunicode_win(s):
+ """Turns bytes into unicode, if needed.
+ """
+ if isinstance(s, bytes):
+ return s.decode(locale.getpreferredencoding())
+ else:
+ return s
+
+
+class SCPClient(object):
+ """
+ An scp1 implementation, compatible with openssh scp.
+ Raises SCPException for all transport related errors. Local filesystem
+ and OS errors pass through.
+
+ Main public methods are .put and .get
+ The get method is controlled by the remote scp instance, and behaves
+ accordingly. This means that symlinks are resolved, and the transfer is
+ halted after too many levels of symlinks are detected.
+ The put method uses os.walk for recursion, and sends files accordingly.
+ Since scp doesn't support symlinks, we send file symlinks as the file
+ (matching scp behaviour), but we make no attempt at symlinked directories.
+ """
+ def __init__(self, transport, buff_size=16384, socket_timeout=5.0,
+ progress=None, sanitize=_sh_quote):
+ """
+ Create an scp1 client.
+
+ @param transport: an existing paramiko L{Transport}
+ @type transport: L{Transport}
+ @param buff_size: size of the scp send buffer.
+ @type buff_size: int
+ @param socket_timeout: channel socket timeout in seconds
+ @type socket_timeout: float
+ @param progress: callback - called with (filename, size, sent) during
+ transfers
+ @param sanitize: function - called with filename, should return
+ safe or escaped string. Uses _sh_quote by default.
+ @type progress: function(string, int, int)
+ """
+ self.transport = transport
+ self.buff_size = buff_size
+ self.socket_timeout = socket_timeout
+ self.channel = None
+ self.preserve_times = False
+ self._progress = progress
+ self._recv_dir = b''
+ self._rename = False
+ self._utime = None
+ self.sanitize = sanitize
+ self._dirtimes = {}
+
+ def __enter__(self):
+ self.channel = self._open()
+ return self
+
+ def __exit__(self, type, value, traceback):
+ self.close()
+
+ def put(self, files, remote_path=b'.',
+ recursive=False, preserve_times=False):
+ """
+ Transfer files to remote host.
+
+ @param files: A single path, or a list of paths to be transfered.
+ recursive must be True to transfer directories.
+ @type files: string OR list of strings
+ @param remote_path: path in which to receive the files on the remote
+ host. defaults to '.'
+ @type remote_path: str
+ @param recursive: transfer files and directories recursively
+ @type recursive: bool
+ @param preserve_times: preserve mtime and atime of transfered files
+ and directories.
+ @type preserve_times: bool
+ """
+ self.preserve_times = preserve_times
+ self.channel = self._open()
+ self._pushed = 0
+ self.channel.settimeout(self.socket_timeout)
+ scp_command = (b'scp -t ', b'scp -r -t ')[recursive]
+ self.channel.exec_command(scp_command +
+ self.sanitize(asbytes(remote_path)))
+ self._recv_confirm()
+
+ if not isinstance(files, (list, tuple)):
+ files = [files]
+
+ if recursive:
+ self._send_recursive(files)
+ else:
+ self._send_files(files)
+
+ self.close()
+
+ def get(self, remote_path, local_path='',
+ recursive=False, preserve_times=False):
+ """
+ Transfer files from remote host to localhost
+
+ @param remote_path: path to retreive from remote host. since this is
+ evaluated by scp on the remote host, shell wildcards and
+ environment variables may be used.
+ @type remote_path: str
+ @param local_path: path in which to receive files locally
+ @type local_path: str
+ @param recursive: transfer files and directories recursively
+ @type recursive: bool
+ @param preserve_times: preserve mtime and atime of transfered files
+ and directories.
+ @type preserve_times: bool
+ """
+ if not isinstance(remote_path, (list, tuple)):
+ remote_path = [remote_path]
+ remote_path = [self.sanitize(asbytes(r)) for r in remote_path]
+ self._recv_dir = local_path or os.getcwd()
+ self._rename = (len(remote_path) == 1 and
+ not os.path.isdir(os.path.abspath(local_path)))
+ if len(remote_path) > 1:
+ if not os.path.exists(self._recv_dir):
+ raise SCPException("Local path '%s' does not exist" %
+ asunicode(self._recv_dir))
+ elif not os.path.isdir(self._recv_dir):
+ raise SCPException("Local path '%s' is not a directory" %
+ asunicode(self._recv_dir))
+ rcsv = (b'', b' -r')[recursive]
+ prsv = (b'', b' -p')[preserve_times]
+ self.channel = self._open()
+ self._pushed = 0
+ self.channel.settimeout(self.socket_timeout)
+ self.channel.exec_command(b"scp" +
+ rcsv +
+ prsv +
+ b" -f " +
+ b' '.join(remote_path))
+ self._recv_all()
+ self.close()
+
+ def _open(self):
+ """open a scp channel"""
+ if self.channel is None:
+ self.channel = self.transport.open_session()
+
+ return self.channel
+
+ def close(self):
+ """close scp channel"""
+ if self.channel is not None:
+ self.channel.close()
+ self.channel = None
+
+ def _read_stats(self, name):
+ """return just the file stats needed for scp"""
+ if os.name == 'nt':
+ name = asunicode(name)
+ stats = os.stat(name)
+ mode = oct(stats.st_mode)[-4:]
+ size = stats.st_size
+ atime = int(stats.st_atime)
+ mtime = int(stats.st_mtime)
+ return (mode, size, mtime, atime)
+
+ def _send_files(self, files):
+ for name in files:
+ basename = asbytes(os.path.basename(name))
+ (mode, size, mtime, atime) = self._read_stats(name)
+ if self.preserve_times:
+ self._send_time(mtime, atime)
+ file_hdl = open(name, 'rb')
+
+ # The protocol can't handle \n in the filename.
+ # Quote them as the control sequence \^J for now,
+ # which is how openssh handles it.
+ self.channel.sendall(("C%s %d " % (mode, size)).encode('ascii') +
+ basename.replace(b'\n', b'\\^J') + b"\n")
+ self._recv_confirm()
+ file_pos = 0
+ if self._progress:
+ if size == 0:
+ # avoid divide-by-zero
+ self._progress(basename, 1, 1)
+ else:
+ self._progress(basename, size, 0)
+ buff_size = self.buff_size
+ chan = self.channel
+ while file_pos < size:
+ chan.sendall(file_hdl.read(buff_size))
+ file_pos = file_hdl.tell()
+ if self._progress:
+ self._progress(basename, size, file_pos)
+ chan.sendall('\x00')
+ file_hdl.close()
+ self._recv_confirm()
+
+ def _chdir(self, from_dir, to_dir):
+ # Pop until we're one level up from our next push.
+ # Push *once* into to_dir.
+ # This is dependent on the depth-first traversal from os.walk
+
+ # add path.sep to each when checking the prefix, so we can use
+ # path.dirname after
+ common = os.path.commonprefix([from_dir + bytes_sep,
+ to_dir + bytes_sep])
+ # now take the dirname, since commonprefix is character based,
+ # and we either have a seperator, or a partial name
+ common = os.path.dirname(common)
+ cur_dir = from_dir.rstrip(bytes_sep)
+ while cur_dir != common:
+ cur_dir = os.path.split(cur_dir)[0]
+ self._send_popd()
+ # now we're in our common base directory, so on
+ self._send_pushd(to_dir)
+
+ def _send_recursive(self, files):
+ for base in files:
+ if not os.path.isdir(base):
+ # filename mixed into the bunch
+ self._send_files([base])
+ continue
+ last_dir = asbytes(base)
+ for root, dirs, fls in os.walk(base):
+ self._chdir(last_dir, asbytes(root))
+ self._send_files([os.path.join(root, f) for f in fls])
+ last_dir = asbytes(root)
+ # back out of the directory
+ while self._pushed > 0:
+ self._send_popd()
+
+ def _send_pushd(self, directory):
+ (mode, size, mtime, atime) = self._read_stats(directory)
+ basename = asbytes(os.path.basename(directory))
+ if self.preserve_times:
+ self._send_time(mtime, atime)
+ self.channel.sendall(('D%s 0 ' % mode).encode('ascii') +
+ basename.replace(b'\n', b'\\^J') + b'\n')
+ self._recv_confirm()
+ self._pushed += 1
+
+ def _send_popd(self):
+ self.channel.sendall('E\n')
+ self._recv_confirm()
+ self._pushed -= 1
+
+ def _send_time(self, mtime, atime):
+ self.channel.sendall(('T%d 0 %d 0\n' % (mtime, atime)).encode('ascii'))
+ self._recv_confirm()
+
+ def _recv_confirm(self):
+ # read scp response
+ msg = b''
+ try:
+ msg = self.channel.recv(512)
+ except SocketTimeout:
+ raise SCPException('Timout waiting for scp response')
+ # slice off the first byte, so this compare will work in py2 and py3
+ if msg and msg[0:1] == b'\x00':
+ return
+ elif msg and msg[0:1] == b'\x01':
+ raise SCPException(asunicode(msg[1:]))
+ elif self.channel.recv_stderr_ready():
+ msg = self.channel.recv_stderr(512)
+ raise SCPException(asunicode(msg))
+ elif not msg:
+ raise SCPException('No response from server')
+ else:
+ raise SCPException('Invalid response from server', msg)
+
+ def _recv_all(self):
+ # loop over scp commands, and receive as necessary
+ command = {b'C': self._recv_file,
+ b'T': self._set_time,
+ b'D': self._recv_pushd,
+ b'E': self._recv_popd}
+ while not self.channel.closed:
+ # wait for command as long as we're open
+ self.channel.sendall('\x00')
+ msg = self.channel.recv(1024)
+ if not msg: # chan closed while recving
+ break
+ assert msg[-1:] == b'\n'
+ msg = msg[:-1]
+ code = msg[0:1]
+ try:
+ command[code](msg[1:])
+ except KeyError:
+ raise SCPException(asunicode(msg[1:]))
+ # directory times can't be set until we're done writing files
+ self._set_dirtimes()
+
+ def _set_time(self, cmd):
+ try:
+ times = cmd.split(b' ')
+ mtime = int(times[0])
+ atime = int(times[2]) or mtime
+ except:
+ self.channel.send(b'\x01')
+ raise SCPException('Bad time format')
+ # save for later
+ self._utime = (atime, mtime)
+
+ def _recv_file(self, cmd):
+ chan = self.channel
+ parts = cmd.strip().split(b' ', 2)
+
+ try:
+ mode = int(parts[0], 8)
+ size = int(parts[1])
+ if self._rename:
+ path = self._recv_dir
+ self._rename = False
+ elif os.name == 'nt':
+ path = os.path.join(asunicode_win(self._recv_dir),
+ parts[2].decode('utf-8'))
+ else:
+ path = os.path.join(asbytes(self._recv_dir),
+ parts[2])
+ except:
+ chan.send('\x01')
+ chan.close()
+ raise SCPException('Bad file format')
+
+ try:
+ file_hdl = open(path, 'wb')
+ except IOError as e:
+ chan.send(b'\x01' + str(e).encode('utf-8'))
+ chan.close()
+ raise
+
+ if self._progress:
+ if size == 0:
+ # avoid divide-by-zero
+ self._progress(path, 1, 1)
+ else:
+ self._progress(path, size, 0)
+ buff_size = self.buff_size
+ pos = 0
+ chan.send(b'\x00')
+ try:
+ while pos < size:
+ # we have to make sure we don't read the final byte
+ if size - pos <= buff_size:
+ buff_size = size - pos
+ file_hdl.write(chan.recv(buff_size))
+ pos = file_hdl.tell()
+ if self._progress:
+ self._progress(path, size, pos)
+
+ msg = chan.recv(512)
+ if msg and msg[0:1] != b'\x00':
+ raise SCPException(asunicode(msg[1:]))
+ except SocketTimeout:
+ chan.close()
+ raise SCPException('Error receiving, socket.timeout')
+
+ file_hdl.truncate()
+ try:
+ os.utime(path, self._utime)
+ self._utime = None
+ os.chmod(path, mode)
+ # should we notify the other end?
+ finally:
+ file_hdl.close()
+ # '\x00' confirmation sent in _recv_all
+
+ def _recv_pushd(self, cmd):
+ parts = cmd.split(b' ', 2)
+ try:
+ mode = int(parts[0], 8)
+ if self._rename:
+ path = self._recv_dir
+ self._rename = False
+ elif os.name == 'nt':
+ path = os.path.join(asunicode_win(self._recv_dir),
+ parts[2].decode('utf-8'))
+ else:
+ path = os.path.join(asbytes(self._recv_dir),
+ parts[2])
+ except:
+ self.channel.send(b'\x01')
+ raise SCPException('Bad directory format')
+ try:
+ if not os.path.exists(path):
+ os.mkdir(path, mode)
+ elif os.path.isdir(path):
+ os.chmod(path, mode)
+ else:
+ raise SCPException('%s: Not a directory' % path)
+ self._dirtimes[path] = (self._utime)
+ self._utime = None
+ self._recv_dir = path
+ except (OSError, SCPException) as e:
+ self.channel.send(b'\x01' + asbytes(str(e)))
+ raise
+
+ def _recv_popd(self, *cmd):
+ self._recv_dir = os.path.split(self._recv_dir)[0]
+
+ def _set_dirtimes(self):
+ try:
+ for d in self._dirtimes:
+ os.utime(d, self._dirtimes[d])
+ finally:
+ self._dirtimes = {}
+
+
+class SCPException(Exception):
+ """SCP exception class"""
+ pass
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..861a9f5
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build =
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..b1a524b
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+import io
+import os
+from setuptools import setup
+
+
+# Need to specify encoding for PY3
+with io.open('README.rst', encoding='utf-8') as fp:
+ description = fp.read()
+setup(
+ name = 'scp',
+ version = '0.10.2',
+ author = 'James Bardin',
+ author_email = 'j.bardin at gmail.com',
+ license = 'LGPL',
+ url = 'https://github.com/jbardin/scp.py',
+ description='scp module for paramiko',
+ long_description=description,
+ py_modules = ['scp'],
+ install_requires = ['paramiko'],
+)
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-scp.git
More information about the Python-modules-commits
mailing list