diff -Nru dockerpty-0.3.4/debian/changelog dockerpty-0.4.1/debian/changelog --- dockerpty-0.3.4/debian/changelog 2015-07-22 06:41:28.000000000 +0000 +++ dockerpty-0.4.1/debian/changelog 2017-03-23 09:11:22.000000000 +0000 @@ -1,8 +1,16 @@ -dockerpty (0.3.4-1build1) wily; urgency=medium +dockerpty (0.4.1-1~16.04.1) xenial; urgency=medium - * No-change rebuild for python3.5 transition + * Build for Xenial (LP: #1637223) - -- Steve Langasek Wed, 22 Jul 2015 06:41:28 +0000 + -- Michael Hudson-Doyle Thu, 23 Mar 2017 21:40:35 +1300 + +dockerpty (0.4.1-1) unstable; urgency=medium + + * New upstream release + * Update Vcs-Git to use https transport + * Bump Standards-Version to 3.9.7, no changes needed + + -- Jason Pleau Thu, 25 Feb 2016 09:01:21 -0500 dockerpty (0.3.4-1) unstable; urgency=medium diff -Nru dockerpty-0.3.4/debian/control dockerpty-0.4.1/debian/control --- dockerpty-0.3.4/debian/control 2015-07-22 06:41:28.000000000 +0000 +++ dockerpty-0.4.1/debian/control 2016-02-29 12:04:28.000000000 +0000 @@ -1,6 +1,5 @@ Source: dockerpty -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Debian Python Modules Team +Maintainer: Debian Python Modules Team Uploaders: Jason Pleau Section: python Priority: optional @@ -10,10 +9,10 @@ python-setuptools, python3-all (>= 3.3), python3-setuptools -Standards-Version: 3.9.6 +Standards-Version: 3.9.7 Homepage: https://github.com/d11wtq/dockerpty -Vcs-Git: git://anonscm.debian.org/python-modules/packages/dockerpty.git -Vcs-Browser: http://anonscm.debian.org/cgit/python-modules/packages/dockerpty.git +Vcs-Git: https://anonscm.debian.org/cgit/python-modules/packages/dockerpty.git +Vcs-Browser: https://anonscm.debian.org/cgit/python-modules/packages/dockerpty.git X-Python-Version: >= 2.7 X-Python3-Version: >= 3.3 diff -Nru dockerpty-0.3.4/debian/.git-dpm dockerpty-0.4.1/debian/.git-dpm --- dockerpty-0.3.4/debian/.git-dpm 2015-06-13 14:09:14.000000000 +0000 +++ dockerpty-0.4.1/debian/.git-dpm 2016-02-29 12:04:28.000000000 +0000 @@ -1,8 +1,11 @@ # see git-dpm(1) from git-dpm package -9ed6f664487fe94a94eeb8830234b7dbe6baa1a3 -9ed6f664487fe94a94eeb8830234b7dbe6baa1a3 -9ed6f664487fe94a94eeb8830234b7dbe6baa1a3 -9ed6f664487fe94a94eeb8830234b7dbe6baa1a3 -dockerpty_0.3.4.orig.tar.gz -50029f80aae4f21e0d7f1471e1fcb701e41f1fdb -13045 +334e6b24293ed09a4e44c65b4f5ec40498af3bb2 +334e6b24293ed09a4e44c65b4f5ec40498af3bb2 +334e6b24293ed09a4e44c65b4f5ec40498af3bb2 +334e6b24293ed09a4e44c65b4f5ec40498af3bb2 +dockerpty_0.4.1.orig.tar.gz +a4c9fa9b582ff3a723e7eec7eeddb3afcb7eb78a +13924 +debianTag="debian/%e%v" +patchedTag="patched/%e%v" +upstreamTag="upstream/%e%u" diff -Nru dockerpty-0.3.4/dockerpty/__init__.py dockerpty-0.4.1/dockerpty/__init__.py --- dockerpty-0.3.4/dockerpty/__init__.py 2015-06-08 15:04:29.000000000 +0000 +++ dockerpty-0.4.1/dockerpty/__init__.py 2016-02-04 14:33:15.000000000 +0000 @@ -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 -Nru dockerpty-0.3.4/dockerpty/pty.py dockerpty-0.4.1/dockerpty/pty.py --- dockerpty-0.3.4/dockerpty/pty.py 2015-06-08 20:52:14.000000000 +0000 +++ dockerpty-0.4.1/dockerpty/pty.py 2016-02-04 18:56:56.000000000 +0000 @@ -16,6 +16,7 @@ import sys import signal +import warnings from ssl import SSLError import dockerpty.io as io @@ -38,7 +39,6 @@ self.pty = pty self.original_handler = None - def __enter__(self): """ Invoked on entering a `with` block. @@ -47,7 +47,6 @@ self.start() return self - def __exit__(self, *_): """ Invoked on exiting a `with` block. @@ -55,7 +54,6 @@ self.stop() - def start(self): """ Start trapping WINCH signals and resizing the PTY. @@ -70,7 +68,6 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ """ 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 @@ 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 @@ 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 @@ 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 -Nru dockerpty-0.3.4/dockerpty.egg-info/PKG-INFO dockerpty-0.4.1/dockerpty.egg-info/PKG-INFO --- dockerpty-0.3.4/dockerpty.egg-info/PKG-INFO 2015-06-08 20:54:40.000000000 +0000 +++ dockerpty-0.4.1/dockerpty.egg-info/PKG-INFO 2016-02-04 18:57:10.000000000 +0000 @@ -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 -Nru dockerpty-0.3.4/PKG-INFO dockerpty-0.4.1/PKG-INFO --- dockerpty-0.3.4/PKG-INFO 2015-06-08 20:54:41.000000000 +0000 +++ dockerpty-0.4.1/PKG-INFO 2016-02-04 18:57:10.000000000 +0000 @@ -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 -Nru dockerpty-0.3.4/setup.py dockerpty-0.4.1/setup.py --- dockerpty-0.3.4/setup.py 2015-06-08 20:52:44.000000000 +0000 +++ dockerpty-0.4.1/setup.py 2016-02-04 18:56:56.000000000 +0000 @@ -27,7 +27,7 @@ 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',