[Python-modules-commits] [dockerpty] 01/03: Import dockerpty_0.4.1.orig.tar.gz

Jason Pleau jpleau-guest at moszumanska.debian.org
Sun Feb 7 05:10:58 UTC 2016


This is an automated email from the git hooks/post-receive script.

jpleau-guest pushed a commit to branch master
in repository dockerpty.

commit 334e6b24293ed09a4e44c65b4f5ec40498af3bb2
Author: Jason Pleau <jason at jpleau.ca>
Date:   Sat Feb 6 23:00:26 2016 -0500

    Import dockerpty_0.4.1.orig.tar.gz
---
 PKG-INFO                    |   2 +-
 dockerpty.egg-info/PKG-INFO |   2 +-
 dockerpty/__init__.py       |  29 +++++-
 dockerpty/pty.py            | 247 +++++++++++++++++++++++++++++++++-----------
 setup.py                    |   2 +-
 5 files changed, 217 insertions(+), 65 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index 09b8a7b..279b7d8 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: dockerpty
-Version: 0.3.4
+Version: 0.4.1
 Summary: Python library to use the pseudo-tty of a docker container
 Home-page: https://github.com/d11wtq/dockerpty
 Author: Chris Corbyn
diff --git a/dockerpty.egg-info/PKG-INFO b/dockerpty.egg-info/PKG-INFO
index 09b8a7b..279b7d8 100644
--- a/dockerpty.egg-info/PKG-INFO
+++ b/dockerpty.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: dockerpty
-Version: 0.3.4
+Version: 0.4.1
 Summary: Python library to use the pseudo-tty of a docker container
 Home-page: https://github.com/d11wtq/dockerpty
 Author: Chris Corbyn
diff --git a/dockerpty/__init__.py b/dockerpty/__init__.py
index 5e7e306..1dba089 100644
--- a/dockerpty/__init__.py
+++ b/dockerpty/__init__.py
@@ -14,14 +14,37 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from dockerpty.pty import PseudoTerminal
+from dockerpty.pty import PseudoTerminal, RunOperation, ExecOperation, exec_create
 
 
-def start(client, container, interactive=True, stdout=None, stderr=None, stdin=None):
+def start(client, container, interactive=True, stdout=None, stderr=None, stdin=None, logs=None):
     """
     Present the PTY of the container inside the current process.
 
     This is just a wrapper for PseudoTerminal(client, container).start()
     """
 
-    PseudoTerminal(client, container, interactive=interactive, stdout=stdout, stderr=stderr, stdin=stdin).start()
+    operation = RunOperation(client, container, interactive=interactive, stdout=stdout,
+                             stderr=stderr, stdin=stdin, logs=logs)
+
+    PseudoTerminal(client, operation).start()
+
+
+def exec_command(
+        client, container, command, interactive=True, stdout=None, stderr=None, stdin=None):
+    """
+    Run provided command via exec API in provided container.
+
+    This is just a wrapper for PseudoTerminal(client, container).exec_command()
+    """
+    exec_id = exec_create(client, container, command, interactive=interactive)
+
+    operation = ExecOperation(client, exec_id,
+                              interactive=interactive, stdout=stdout, stderr=stderr, stdin=stdin)
+    PseudoTerminal(client, operation).start()
+
+
+def start_exec(client, exec_id, interactive=True, stdout=None, stderr=None, stdin=None):
+    operation = ExecOperation(client, exec_id,
+                              interactive=interactive, stdout=stdout, stderr=stderr, stdin=stdin)
+    PseudoTerminal(client, operation).start()
diff --git a/dockerpty/pty.py b/dockerpty/pty.py
index febe684..25cb788 100644
--- a/dockerpty/pty.py
+++ b/dockerpty/pty.py
@@ -16,6 +16,7 @@
 
 import sys
 import signal
+import warnings
 from ssl import SSLError
 
 import dockerpty.io as io
@@ -38,7 +39,6 @@ class WINCHHandler(object):
         self.pty = pty
         self.original_handler = None
 
-
     def __enter__(self):
         """
         Invoked on entering a `with` block.
@@ -47,7 +47,6 @@ class WINCHHandler(object):
         self.start()
         return self
 
-
     def __exit__(self, *_):
         """
         Invoked on exiting a `with` block.
@@ -55,7 +54,6 @@ class WINCHHandler(object):
 
         self.stop()
 
-
     def start(self):
         """
         Start trapping WINCH signals and resizing the PTY.
@@ -70,7 +68,6 @@ class WINCHHandler(object):
 
         self.original_handler = signal.signal(signal.SIGWINCH, handle)
 
-
     def stop(self):
         """
         Stop trapping WINCH signals and restore the previous WINCH handler.
@@ -80,40 +77,45 @@ class WINCHHandler(object):
             signal.signal(signal.SIGWINCH, self.original_handler)
 
 
-class PseudoTerminal(object):
-    """
-    Wraps the pseudo-TTY (PTY) allocated to a docker container.
+class Operation(object):
 
-    The PTY is managed via the current process' TTY until it is closed.
+    def israw(self, **kwargs):
+        """
+        are we dealing with a tty or not?
+        """
+        raise NotImplementedError()
 
-    Example:
+    def start(self, **kwargs):
+        """
+        start execution
+        """
+        raise NotImplementedError()
 
-        import docker
-        from dockerpty import PseudoTerminal
+    def resize(self, height, width, **kwargs):
+        """
+        if we have terminal, resize it
+        """
+        raise NotImplementedError()
 
-        client = docker.Client()
-        container = client.create_container(
-            image='busybox:latest',
-            stdin_open=True,
-            tty=True,
-            command='/bin/sh',
-        )
+    def sockets(self):
+        """Return sockets for streams."""
+        raise NotImplementedError()
 
-        # hijacks the current tty until the pty is closed
-        PseudoTerminal(client, container).start()
 
-    Care is taken to ensure all file descriptors are restored on exit. For
-    example, you can attach to a running container from within a Python REPL
-    and when the container exits, the user will be returned to the Python REPL
-    without adverse effects.
+class RunOperation(Operation):
+    """
+    class for handling `docker run`-like command
     """
 
-
-    def __init__(self, client, container, interactive=True, stdout=None, stderr=None, stdin=None):
+    def __init__(self, client, container, interactive=True, stdout=None, stderr=None, stdin=None, logs=None):
         """
         Initialize the PTY using the docker.Client instance and container dict.
         """
 
+        if logs is None:
+            warnings.warn("The default behaviour of dockerpty is changing. Please add logs=1 to your dockerpty.start call to maintain existing behaviour. See https://github.com/d11wtq/dockerpty/issues/51 for details.", DeprecationWarning)
+            logs = 1
+
         self.client = client
         self.container = container
         self.raw = None
@@ -121,9 +123,9 @@ class PseudoTerminal(object):
         self.stdout = stdout or sys.stdout
         self.stderr = stderr or sys.stderr
         self.stdin = stdin or sys.stdin
+        self.logs = logs
 
-
-    def start(self, **kwargs):
+    def start(self, sockets=None, **kwargs):
         """
         Present the PTY of the container inside the current process.
 
@@ -131,7 +133,7 @@ class PseudoTerminal(object):
         is closed.
         """
 
-        pty_stdin, pty_stdout, pty_stderr = self.sockets()
+        pty_stdin, pty_stdout, pty_stderr = sockets or self.sockets()
         pumps = []
 
         if pty_stdin and self.interactive:
@@ -143,21 +145,12 @@ class PseudoTerminal(object):
         if pty_stderr:
             pumps.append(io.Pump(pty_stderr, io.Stream(self.stderr), propagate_close=False))
 
-        if not self.container_info()['State']['Running']:
+        if not self._container_info()['State']['Running']:
             self.client.start(self.container, **kwargs)
 
-        flags = [p.set_blocking(False) for p in pumps]
+        return pumps
 
-        try:
-            with WINCHHandler(self):
-                self._hijack_tty(pumps)
-        finally:
-            if flags:
-                for (pump, flag) in zip(pumps, flags):
-                    io.set_blocking(pump, flag)
-
-
-    def israw(self):
+    def israw(self, **kwargs):
         """
         Returns True if the PTY should operate in raw mode.
 
@@ -165,12 +158,11 @@ class PseudoTerminal(object):
         """
 
         if self.raw is None:
-            info = self.container_info()
+            info = self._container_info()
             self.raw = self.stdout.isatty() and info['Config']['Tty']
 
         return self.raw
 
-
     def sockets(self):
         """
         Returns a tuple of sockets connected to the pty (stdin,stdout,stderr).
@@ -179,13 +171,13 @@ class PseudoTerminal(object):
         returned in the tuple.
         """
 
-        info = self.container_info()
+        info = self._container_info()
 
         def attach_socket(key):
             if info['Config']['Attach{0}'.format(key.capitalize())]:
                 socket = self.client.attach_socket(
                     self.container,
-                    {key: 1, 'stream': 1, 'logs': 1},
+                    {key: 1, 'stream': 1, 'logs': self.logs},
                 )
                 stream = io.Stream(socket)
 
@@ -198,6 +190,152 @@ class PseudoTerminal(object):
 
         return map(attach_socket, ('stdin', 'stdout', 'stderr'))
 
+    def resize(self, height, width, **kwargs):
+        """
+        resize pty within container
+        """
+        self.client.resize(self.container, height=height, width=width)
+
+    def _container_info(self):
+        """
+        Thin wrapper around client.inspect_container().
+        """
+
+        return self.client.inspect_container(self.container)
+
+
+def exec_create(client, container, command, interactive=True):
+    exec_id = client.exec_create(container, command, tty=interactive, stdin=interactive)
+    return exec_id
+
+
+class ExecOperation(Operation):
+    """
+    class for handling `docker exec`-like command
+    """
+
+    def __init__(self, client, exec_id, interactive=True, stdout=None, stderr=None, stdin=None):
+        self.exec_id = exec_id
+        self.client = client
+        self.raw = None
+        self.interactive = interactive
+        self.stdout = stdout or sys.stdout
+        self.stderr = stderr or sys.stderr
+        self.stdin = stdin or sys.stdin
+        self._info = None
+
+    def start(self, sockets=None, **kwargs):
+        """
+        start execution
+        """
+        stream = sockets or self.sockets()
+        pumps = []
+
+        if self.interactive:
+            pumps.append(io.Pump(io.Stream(self.stdin), stream, wait_for_output=False))
+
+        pumps.append(io.Pump(stream, io.Stream(self.stdout), propagate_close=False))
+        # FIXME: since exec_start returns a single socket, how do we
+        #        distinguish between stdout and stderr?
+        # pumps.append(io.Pump(stream, io.Stream(self.stderr), propagate_close=False))
+
+        return pumps
+
+    def israw(self, **kwargs):
+        """
+        Returns True if the PTY should operate in raw mode.
+
+        If the exec was not started with tty=True, this will return False.
+        """
+
+        if self.raw is None:
+            self.raw = self.stdout.isatty() and self.is_process_tty()
+
+        return self.raw
+
+    def sockets(self):
+        """
+        Return a single socket which is processing all I/O to exec
+        """
+        socket = self.client.exec_start(self.exec_id, socket=True, tty=self.interactive)
+        stream = io.Stream(socket)
+        if self.is_process_tty():
+            return stream
+        else:
+            return io.Demuxer(stream)
+
+    def resize(self, height, width, **kwargs):
+        """
+        resize pty of an execed process
+        """
+        self.client.exec_resize(self.exec_id, height=height, width=width)
+
+    def is_process_tty(self):
+        """
+        does execed process have allocated tty?
+        """
+        return self._exec_info()["ProcessConfig"]["tty"]
+
+    def _exec_info(self):
+        """
+        Caching wrapper around client.exec_inspect
+        """
+        if self._info is None:
+            self._info = self.client.exec_inspect(self.exec_id)
+        return self._info
+
+
+class PseudoTerminal(object):
+    """
+    Wraps the pseudo-TTY (PTY) allocated to a docker container.
+
+    The PTY is managed via the current process' TTY until it is closed.
+
+    Example:
+
+        import docker
+        from dockerpty import PseudoTerminal
+
+        client = docker.Client()
+        container = client.create_container(
+            image='busybox:latest',
+            stdin_open=True,
+            tty=True,
+            command='/bin/sh',
+        )
+
+        # hijacks the current tty until the pty is closed
+        PseudoTerminal(client, container).start()
+
+    Care is taken to ensure all file descriptors are restored on exit. For
+    example, you can attach to a running container from within a Python REPL
+    and when the container exits, the user will be returned to the Python REPL
+    without adverse effects.
+    """
+
+    def __init__(self, client, operation):
+        """
+        Initialize the PTY using the docker.Client instance and container dict.
+        """
+
+        self.client = client
+        self.operation = operation
+
+    def sockets(self):
+        return self.operation.sockets()
+
+    def start(self, sockets=None):
+        pumps = self.operation.start(sockets=sockets)
+
+        flags = [p.set_blocking(False) for p in pumps]
+
+        try:
+            with WINCHHandler(self):
+                self._hijack_tty(pumps)
+        finally:
+            if flags:
+                for (pump, flag) in zip(pumps, flags):
+                    io.set_blocking(pump, flag)
 
     def resize(self, size=None):
         """
@@ -207,29 +345,20 @@ class PseudoTerminal(object):
         it will be determined by the size of the current TTY.
         """
 
-        if not self.israw():
+        if not self.operation.israw():
             return
 
-        size = size or tty.size(self.stdout)
+        size = size or tty.size(self.operation.stdout)
 
         if size is not None:
             rows, cols = size
             try:
-                self.client.resize(self.container, height=rows, width=cols)
-            except IOError: # Container already exited
+                self.operation.resize(height=rows, width=cols)
+            except IOError:  # Container already exited
                 pass
 
-
-    def container_info(self):
-        """
-        Thin wrapper around client.inspect_container().
-        """
-
-        return self.client.inspect_container(self.container)
-
-
     def _hijack_tty(self, pumps):
-        with tty.Terminal(self.stdin, raw=self.israw()):
+        with tty.Terminal(self.operation.stdin, raw=self.operation.israw()):
             self.resize()
             while True:
                 read_pumps = [p for p in pumps if not p.eof]
diff --git a/setup.py b/setup.py
index 8a63aff..a9eaf71 100644
--- a/setup.py
+++ b/setup.py
@@ -27,7 +27,7 @@ def read(filename):
 
 setup(
     name='dockerpty',
-    version='0.3.4',
+    version='0.4.1',
     description='Python library to use the pseudo-tty of a docker container',
     long_description=read('README.md'),
     url='https://github.com/d11wtq/dockerpty',

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/dockerpty.git



More information about the Python-modules-commits mailing list