[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