[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