diff options
author | robot-contrib <robot-contrib@yandex-team.com> | 2023-12-11 10:07:41 +0300 |
---|---|---|
committer | robot-contrib <robot-contrib@yandex-team.com> | 2023-12-11 10:38:05 +0300 |
commit | 425d65a76c5bda62894f93d32f1f5e32f7439539 (patch) | |
tree | 3d2456c8f46be0614854cf42abeaac11ecb0f97e | |
parent | 5699cec00313bfc4f3a81d6c77cd6b1ffa7237d3 (diff) | |
download | ydb-425d65a76c5bda62894f93d32f1f5e32f7439539.tar.gz |
Update contrib/python/pexpect/py3 to 4.9.0
-rw-r--r-- | contrib/python/pexpect/py3/.dist-info/METADATA | 11 | ||||
-rw-r--r-- | contrib/python/pexpect/py3/pexpect/__init__.py | 10 | ||||
-rw-r--r-- | contrib/python/pexpect/py3/pexpect/_async.py | 129 | ||||
-rw-r--r-- | contrib/python/pexpect/py3/pexpect/_async_pre_await.py | 111 | ||||
-rw-r--r-- | contrib/python/pexpect/py3/pexpect/_async_w_await.py | 118 | ||||
-rw-r--r-- | contrib/python/pexpect/py3/pexpect/bashrc.sh | 2 | ||||
-rw-r--r-- | contrib/python/pexpect/py3/pexpect/fdpexpect.py | 6 | ||||
-rw-r--r-- | contrib/python/pexpect/py3/pexpect/popen_spawn.py | 2 | ||||
-rw-r--r-- | contrib/python/pexpect/py3/pexpect/pxssh.py | 13 | ||||
-rw-r--r-- | contrib/python/pexpect/py3/pexpect/replwrap.py | 22 | ||||
-rw-r--r-- | contrib/python/pexpect/py3/pexpect/run.py | 2 | ||||
-rw-r--r-- | contrib/python/pexpect/py3/pexpect/socket_pexpect.py | 145 | ||||
-rw-r--r-- | contrib/python/pexpect/py3/pexpect/spawnbase.py | 13 | ||||
-rw-r--r-- | contrib/python/pexpect/py3/ya.make | 9 |
14 files changed, 468 insertions, 125 deletions
diff --git a/contrib/python/pexpect/py3/.dist-info/METADATA b/contrib/python/pexpect/py3/.dist-info/METADATA index 4c417227db..c5ea4a1a59 100644 --- a/contrib/python/pexpect/py3/.dist-info/METADATA +++ b/contrib/python/pexpect/py3/.dist-info/METADATA @@ -1,11 +1,15 @@ Metadata-Version: 2.1 Name: pexpect -Version: 4.8.0 +Version: 4.9.0 Summary: Pexpect allows easy control of interactive console applications. Home-page: https://pexpect.readthedocs.io/ Author: Noah Spurrier; Thomas Kluyver; Jeff Quast Author-email: noah@noah.org, thomas@kluyver.me.uk, contact@jeffquast.com License: ISC license +Project-URL: Bug Tracker, https://github.com/pexpect/pexpect/issues +Project-URL: Documentation, https://pexpect.readthedocs.io/ +Project-URL: Source Code, https://github.com/pexpect/pexpect +Project-URL: History, https://pexpect.readthedocs.io/en/stable/history.html Platform: UNIX Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console @@ -27,6 +31,7 @@ Classifier: Topic :: System :: Installation/Setup Classifier: Topic :: System :: Shells Classifier: Topic :: System :: Software Distribution Classifier: Topic :: Terminals +License-File: LICENSE Requires-Dist: ptyprocess (>=0.5) @@ -36,7 +41,7 @@ Don Libes' Expect. Pexpect allows your script to spawn a child application and control it as if a human were typing commands. Pexpect can be used for automating interactive applications such as ssh, ftp, -passwd, telnet, etc. It can be used to a automate setup scripts for duplicating +passwd, telnet, etc. It can be used to automate setup scripts for duplicating software package installations on different servers. It can be used for automated software testing. Pexpect is in the spirit of Don Libes' Expect, but Pexpect is pure Python. @@ -45,5 +50,3 @@ The main features of Pexpect require the pty module in the Python standard library, which is only available on Unix-like systems. Some features—waiting for patterns from file descriptors or subprocesses—are also available on Windows. - - diff --git a/contrib/python/pexpect/py3/pexpect/__init__.py b/contrib/python/pexpect/py3/pexpect/__init__.py index 7e30453787..86254ee720 100644 --- a/contrib/python/pexpect/py3/pexpect/__init__.py +++ b/contrib/python/pexpect/py3/pexpect/__init__.py @@ -1,6 +1,6 @@ '''Pexpect is a Python module for spawning child applications and controlling them automatically. Pexpect can be used for automating interactive applications -such as ssh, ftp, passwd, telnet, etc. It can be used to a automate setup +such as ssh, ftp, passwd, telnet, etc. It can be used to automate setup scripts for duplicating software package installations on different servers. It can be used for automated software testing. Pexpect is in the spirit of Don Libes' Expect, but Pexpect is pure Python. Other Expect-like modules for Python @@ -29,6 +29,12 @@ For example:: child.expect('Password:') child.sendline(mypassword) +Context manager can be used for the spawn() function:: + + with pexpect.spawn('scp foo user@example.com:.') as child: + child.expect('Password:') + child.sendline(mypassword) + This works even for commands that ask for passwords or other input outside of the normal stdio streams. For example, ssh reads input directly from the TTY device which bypasses stdin. @@ -75,7 +81,7 @@ if sys.platform != 'win32': from .pty_spawn import spawn, spawnu from .run import run, runu -__version__ = '4.8.0' +__version__ = '4.9.0' __revision__ = '' __all__ = ['ExceptionPexpect', 'EOF', 'TIMEOUT', 'spawn', 'spawnu', 'run', 'runu', 'which', 'split_command_line', '__version__', '__revision__'] diff --git a/contrib/python/pexpect/py3/pexpect/_async.py b/contrib/python/pexpect/py3/pexpect/_async.py index bc83261d25..261720c160 100644 --- a/contrib/python/pexpect/py3/pexpect/_async.py +++ b/contrib/python/pexpect/py3/pexpect/_async.py @@ -1,101 +1,28 @@ -import asyncio -import errno -import signal - -from pexpect import EOF - -async def expect_async(expecter, timeout=None): - # First process data that was previously read - if it maches, we don't need - # async stuff. - idx = expecter.existing_data() - if idx is not None: - return idx - if not expecter.spawn.async_pw_transport: - pw = PatternWaiter() - pw.set_expecter(expecter) - transport, pw = await asyncio.get_event_loop()\ - .connect_read_pipe(lambda: pw, expecter.spawn) - expecter.spawn.async_pw_transport = pw, transport - else: - pw, transport = expecter.spawn.async_pw_transport - pw.set_expecter(expecter) - transport.resume_reading() - try: - return (await asyncio.wait_for(pw.fut, timeout)) - except asyncio.TimeoutError as e: - transport.pause_reading() - return expecter.timeout(e) - -async def repl_run_command_async(repl, cmdlines, timeout=-1): - res = [] - repl.child.sendline(cmdlines[0]) - for line in cmdlines[1:]: - await repl._expect_prompt(timeout=timeout, async_=True) - res.append(repl.child.before) - repl.child.sendline(line) - - # Command was fully submitted, now wait for the next prompt - prompt_idx = await repl._expect_prompt(timeout=timeout, async_=True) - if prompt_idx == 1: - # We got the continuation prompt - command was incomplete - repl.child.kill(signal.SIGINT) - await repl._expect_prompt(timeout=1, async_=True) - raise ValueError("Continuation prompt found - input was incomplete:") - return u''.join(res + [repl.child.before]) - -class PatternWaiter(asyncio.Protocol): - transport = None - - def set_expecter(self, expecter): - self.expecter = expecter - self.fut = asyncio.Future() - - def found(self, result): - if not self.fut.done(): - self.fut.set_result(result) - self.transport.pause_reading() - - def error(self, exc): - if not self.fut.done(): - self.fut.set_exception(exc) - self.transport.pause_reading() - - def connection_made(self, transport): - self.transport = transport - - def data_received(self, data): - spawn = self.expecter.spawn - s = spawn._decoder.decode(data) - spawn._log(s, 'read') - - if self.fut.done(): - spawn._before.write(s) - spawn._buffer.write(s) - return - - try: - index = self.expecter.new_data(s) - if index is not None: - # Found a match - self.found(index) - except Exception as e: - self.expecter.errored() - self.error(e) - - def eof_received(self): - # N.B. If this gets called, async will close the pipe (the spawn object) - # for us - try: - self.expecter.spawn.flag_eof = True - index = self.expecter.eof() - except EOF as e: - self.error(e) - else: - self.found(index) - - def connection_lost(self, exc): - if isinstance(exc, OSError) and exc.errno == errno.EIO: - # We may get here without eof_received being called, e.g on Linux - self.eof_received() - elif exc is not None: - self.error(exc) +"""Facade that provides coroutines implementation pertinent to running Py version. + +Python 3.5 introduced the async def/await syntax keyword. +With later versions coroutines and methods to get the running asyncio loop are +being deprecated, not supported anymore. + +For Python versions later than 3.6, coroutines and objects that are defined via +``async def``/``await`` keywords are imported. + +Here the code is just imported, to provide the same interface to older code. +""" +# pylint: disable=unused-import +# flake8: noqa: F401 +from sys import version_info as py_version_info + +# this assumes async def/await are more stable +if py_version_info >= (3, 6): + from pexpect._async_w_await import ( + PatternWaiter, + expect_async, + repl_run_command_async, + ) +else: + from pexpect._async_pre_await import ( + PatternWaiter, + expect_async, + repl_run_command_async, + ) diff --git a/contrib/python/pexpect/py3/pexpect/_async_pre_await.py b/contrib/python/pexpect/py3/pexpect/_async_pre_await.py new file mode 100644 index 0000000000..81ece1b6da --- /dev/null +++ b/contrib/python/pexpect/py3/pexpect/_async_pre_await.py @@ -0,0 +1,111 @@ +"""Implementation of coroutines without using ``async def``/``await`` keywords. + +``@asyncio.coroutine`` and ``yield from`` are used here instead. +""" +import asyncio +import errno +import signal + +from pexpect import EOF + + +@asyncio.coroutine +def expect_async(expecter, timeout=None): + # First process data that was previously read - if it maches, we don't need + # async stuff. + idx = expecter.existing_data() + if idx is not None: + return idx + if not expecter.spawn.async_pw_transport: + pw = PatternWaiter() + pw.set_expecter(expecter) + transport, pw = yield from asyncio.get_event_loop().connect_read_pipe( + lambda: pw, expecter.spawn + ) + expecter.spawn.async_pw_transport = pw, transport + else: + pw, transport = expecter.spawn.async_pw_transport + pw.set_expecter(expecter) + transport.resume_reading() + try: + return (yield from asyncio.wait_for(pw.fut, timeout)) + except asyncio.TimeoutError as e: + transport.pause_reading() + return expecter.timeout(e) + + +@asyncio.coroutine +def repl_run_command_async(repl, cmdlines, timeout=-1): + res = [] + repl.child.sendline(cmdlines[0]) + for line in cmdlines[1:]: + yield from repl._expect_prompt(timeout=timeout, async_=True) + res.append(repl.child.before) + repl.child.sendline(line) + + # Command was fully submitted, now wait for the next prompt + prompt_idx = yield from repl._expect_prompt(timeout=timeout, async_=True) + if prompt_idx == 1: + # We got the continuation prompt - command was incomplete + repl.child.kill(signal.SIGINT) + yield from repl._expect_prompt(timeout=1, async_=True) + raise ValueError("Continuation prompt found - input was incomplete:") + return "".join(res + [repl.child.before]) + + +class PatternWaiter(asyncio.Protocol): + transport = None + + def set_expecter(self, expecter): + self.expecter = expecter + self.fut = asyncio.Future() + + def found(self, result): + if not self.fut.done(): + self.fut.set_result(result) + self.transport.pause_reading() + + def error(self, exc): + if not self.fut.done(): + self.fut.set_exception(exc) + self.transport.pause_reading() + + def connection_made(self, transport): + self.transport = transport + + def data_received(self, data): + spawn = self.expecter.spawn + s = spawn._decoder.decode(data) + spawn._log(s, "read") + + if self.fut.done(): + spawn._before.write(s) + spawn._buffer.write(s) + return + + try: + index = self.expecter.new_data(s) + if index is not None: + # Found a match + self.found(index) + except Exception as e: + self.expecter.errored() + self.error(e) + + def eof_received(self): + # N.B. If this gets called, async will close the pipe (the spawn object) + # for us + try: + self.expecter.spawn.flag_eof = True + index = self.expecter.eof() + except EOF as e: + self.error(e) + else: + self.found(index) + + def connection_lost(self, exc): + if isinstance(exc, OSError) and exc.errno == errno.EIO: + # We may get here without eof_received being called, e.g on Linux + self.eof_received() + elif exc is not None: + self.error(exc) diff --git a/contrib/python/pexpect/py3/pexpect/_async_w_await.py b/contrib/python/pexpect/py3/pexpect/_async_w_await.py new file mode 100644 index 0000000000..59cb1ef13d --- /dev/null +++ b/contrib/python/pexpect/py3/pexpect/_async_w_await.py @@ -0,0 +1,118 @@ +"""Implementation of coroutines using ``async def``/``await`` keywords. + +These keywords replaced ``@asyncio.coroutine`` and ``yield from`` from +Python 3.5 onwards. +""" +import asyncio +import errno +import signal +from sys import version_info as py_version_info + +from pexpect import EOF + +if py_version_info >= (3, 7): + # get_running_loop, new in 3.7, is preferred to get_event_loop + _loop_getter = asyncio.get_running_loop +else: + # Deprecation warning since 3.10 + _loop_getter = asyncio.get_event_loop + + +async def expect_async(expecter, timeout=None): + # First process data that was previously read - if it maches, we don't need + # async stuff. + idx = expecter.existing_data() + if idx is not None: + return idx + if not expecter.spawn.async_pw_transport: + pattern_waiter = PatternWaiter() + pattern_waiter.set_expecter(expecter) + transport, pattern_waiter = await _loop_getter().connect_read_pipe( + lambda: pattern_waiter, expecter.spawn + ) + expecter.spawn.async_pw_transport = pattern_waiter, transport + else: + pattern_waiter, transport = expecter.spawn.async_pw_transport + pattern_waiter.set_expecter(expecter) + transport.resume_reading() + try: + return await asyncio.wait_for(pattern_waiter.fut, timeout) + except asyncio.TimeoutError as exc: + transport.pause_reading() + return expecter.timeout(exc) + + +async def repl_run_command_async(repl, cmdlines, timeout=-1): + res = [] + repl.child.sendline(cmdlines[0]) + for line in cmdlines[1:]: + await repl._expect_prompt(timeout=timeout, async_=True) + res.append(repl.child.before) + repl.child.sendline(line) + + # Command was fully submitted, now wait for the next prompt + prompt_idx = await repl._expect_prompt(timeout=timeout, async_=True) + if prompt_idx == 1: + # We got the continuation prompt - command was incomplete + repl.child.kill(signal.SIGINT) + await repl._expect_prompt(timeout=1, async_=True) + raise ValueError("Continuation prompt found - input was incomplete:") + return "".join(res + [repl.child.before]) + + +class PatternWaiter(asyncio.Protocol): + transport = None + + def set_expecter(self, expecter): + self.expecter = expecter + self.fut = asyncio.Future() + + def found(self, result): + if not self.fut.done(): + self.fut.set_result(result) + self.transport.pause_reading() + + def error(self, exc): + if not self.fut.done(): + self.fut.set_exception(exc) + self.transport.pause_reading() + + def connection_made(self, transport): + self.transport = transport + + def data_received(self, data): + spawn = self.expecter.spawn + s = spawn._decoder.decode(data) + spawn._log(s, "read") + + if self.fut.done(): + spawn._before.write(s) + spawn._buffer.write(s) + return + + try: + index = self.expecter.new_data(s) + if index is not None: + # Found a match + self.found(index) + except Exception as exc: + self.expecter.errored() + self.error(exc) + + def eof_received(self): + # N.B. If this gets called, async will close the pipe (the spawn object) + # for us + try: + self.expecter.spawn.flag_eof = True + index = self.expecter.eof() + except EOF as exc: + self.error(exc) + else: + self.found(index) + + def connection_lost(self, exc): + if isinstance(exc, OSError) and exc.errno == errno.EIO: + # We may get here without eof_received being called, e.g on Linux + self.eof_received() + elif exc is not None: + self.error(exc) diff --git a/contrib/python/pexpect/py3/pexpect/bashrc.sh b/contrib/python/pexpect/py3/pexpect/bashrc.sh index c734ac90b8..d75d1a5b62 100644 --- a/contrib/python/pexpect/py3/pexpect/bashrc.sh +++ b/contrib/python/pexpect/py3/pexpect/bashrc.sh @@ -14,3 +14,5 @@ PS1="$" # Unset PROMPT_COMMAND, so that it can't change PS1 to something unexpected. unset PROMPT_COMMAND + +bind 'set enable-bracketed-paste off' diff --git a/contrib/python/pexpect/py3/pexpect/fdpexpect.py b/contrib/python/pexpect/py3/pexpect/fdpexpect.py index cddd50e100..140bdfeeda 100644 --- a/contrib/python/pexpect/py3/pexpect/fdpexpect.py +++ b/contrib/python/pexpect/py3/pexpect/fdpexpect.py @@ -1,7 +1,11 @@ -'''This is like pexpect, but it will work with any file descriptor that you +'''This is like :mod:`pexpect`, but it will work with any file descriptor that you pass it. You are responsible for opening and close the file descriptor. This allows you to use Pexpect with sockets and named pipes (FIFOs). +.. note:: + socket.fileno() does not give a readable file descriptor on windows. + Use :mod:`pexpect.socket_pexpect` for cross-platform socket support + PEXPECT LICENSE This license is approved by the OSI and FSF as GPL-compatible. diff --git a/contrib/python/pexpect/py3/pexpect/popen_spawn.py b/contrib/python/pexpect/py3/pexpect/popen_spawn.py index 4bb58cfe76..e6bdf07d61 100644 --- a/contrib/python/pexpect/py3/pexpect/popen_spawn.py +++ b/contrib/python/pexpect/py3/pexpect/popen_spawn.py @@ -57,7 +57,7 @@ class PopenSpawn(SpawnBase): self._read_queue = Queue() self._read_thread = threading.Thread(target=self._read_incoming) - self._read_thread.setDaemon(True) + self._read_thread.daemon = True self._read_thread.start() _read_reached_eof = False diff --git a/contrib/python/pexpect/py3/pexpect/pxssh.py b/contrib/python/pexpect/py3/pexpect/pxssh.py index 3d53bd9746..742f59e406 100644 --- a/contrib/python/pexpect/py3/pexpect/pxssh.py +++ b/contrib/python/pexpect/py3/pexpect/pxssh.py @@ -143,8 +143,8 @@ class pxssh (spawn): # used to set shell command-line prompt to UNIQUE_PROMPT. self.PROMPT_SET_SH = r"PS1='[PEXPECT]\$ '" self.PROMPT_SET_CSH = r"set prompt='[PEXPECT]\$ '" - self.SSH_OPTS = ("-o'RSAAuthentication=no'" - + " -o 'PubkeyAuthentication=no'") + self.PROMPT_SET_ZSH = "prompt restore;\nPS1='[PEXPECT]%(!.#.$) '" + self.SSH_OPTS = (" -o 'PubkeyAuthentication=no'") # Disabling host key checking, makes you vulnerable to MITM attacks. # + " -o 'StrictHostKeyChecking=no'" # + " -o 'UserKnownHostsFile /dev/null' ") @@ -152,7 +152,7 @@ class pxssh (spawn): # displaying a GUI password dialog. I have not figured out how to # disable only SSH_ASKPASS without also disabling X11 forwarding. # Unsetting SSH_ASKPASS on the remote side doesn't disable it! Annoying! - #self.SSH_OPTS = "-x -o'RSAAuthentication=no' -o 'PubkeyAuthentication=no'" + #self.SSH_OPTS = "-x -o 'PubkeyAuthentication=no'" self.force_password = False self.debug_command_string = debug_command_string @@ -530,8 +530,11 @@ class pxssh (spawn): if i == 0: # csh-style self.sendline(self.PROMPT_SET_CSH) i = self.expect([TIMEOUT, self.PROMPT], timeout=10) - if i == 0: - return False + if i == 0: # zsh-style + self.sendline(self.PROMPT_SET_ZSH) + i = self.expect([TIMEOUT, self.PROMPT], timeout=10) + if i == 0: + return False return True # vi:ts=4:sw=4:expandtab:ft=python: diff --git a/contrib/python/pexpect/py3/pexpect/replwrap.py b/contrib/python/pexpect/py3/pexpect/replwrap.py index c930f1e4fe..08dbd5e869 100644 --- a/contrib/python/pexpect/py3/pexpect/replwrap.py +++ b/contrib/python/pexpect/py3/pexpect/replwrap.py @@ -108,23 +108,29 @@ class REPLWrapper(object): + command) return u''.join(res + [self.child.before]) -def python(command="python"): +def python(command=sys.executable): """Start a Python shell and return a :class:`REPLWrapper` object.""" return REPLWrapper(command, u">>> ", u"import sys; sys.ps1={0!r}; sys.ps2={1!r}") -def bash(command="bash"): - """Start a bash shell and return a :class:`REPLWrapper` object.""" - bashrc = os.path.join(os.path.dirname(__file__), 'bashrc.sh') - child = pexpect.spawn(command, ['--rcfile', bashrc], echo=False, - encoding='utf-8') +def _repl_sh(command, args, non_printable_insert): + child = pexpect.spawn(command, args, echo=False, encoding='utf-8') # If the user runs 'env', the value of PS1 will be in the output. To avoid # replwrap seeing that as the next prompt, we'll embed the marker characters # for invisible characters in the prompt; these show up when inspecting the # environment variable, but not when bash displays the prompt. - ps1 = PEXPECT_PROMPT[:5] + u'\\[\\]' + PEXPECT_PROMPT[5:] - ps2 = PEXPECT_CONTINUATION_PROMPT[:5] + u'\\[\\]' + PEXPECT_CONTINUATION_PROMPT[5:] + ps1 = PEXPECT_PROMPT[:5] + non_printable_insert + PEXPECT_PROMPT[5:] + ps2 = PEXPECT_CONTINUATION_PROMPT[:5] + non_printable_insert + PEXPECT_CONTINUATION_PROMPT[5:] prompt_change = u"PS1='{0}' PS2='{1}' PROMPT_COMMAND=''".format(ps1, ps2) return REPLWrapper(child, u'\\$', prompt_change, extra_init_cmd="export PAGER=cat") + +def bash(command="bash"): + """Start a bash shell and return a :class:`REPLWrapper` object.""" + bashrc = os.path.join(os.path.dirname(__file__), 'bashrc.sh') + return _repl_sh(command, ['--rcfile', bashrc], non_printable_insert='\\[\\]') + +def zsh(command="zsh", args=("--no-rcs", "-V", "+Z")): + """Start a zsh shell and return a :class:`REPLWrapper` object.""" + return _repl_sh(command, list(args), non_printable_insert='%(!..)') diff --git a/contrib/python/pexpect/py3/pexpect/run.py b/contrib/python/pexpect/py3/pexpect/run.py index ff288a1246..5695ab7f7b 100644 --- a/contrib/python/pexpect/py3/pexpect/run.py +++ b/contrib/python/pexpect/py3/pexpect/run.py @@ -66,7 +66,7 @@ def run(command, timeout=30, withexitstatus=False, events=None, The 'events' argument should be either a dictionary or a tuple list that contains patterns and responses. Whenever one of the patterns is seen in the command output, run() will send the associated response string. - So, run() in the above example can be also written as: + So, run() in the above example can be also written as:: run("mencoder dvd://1 -o video.avi -oac copy -ovc copy", events=[(TIMEOUT,print_ticks)], timeout=5) diff --git a/contrib/python/pexpect/py3/pexpect/socket_pexpect.py b/contrib/python/pexpect/py3/pexpect/socket_pexpect.py new file mode 100644 index 0000000000..cb11ac2258 --- /dev/null +++ b/contrib/python/pexpect/py3/pexpect/socket_pexpect.py @@ -0,0 +1,145 @@ +"""This is like :mod:`pexpect`, but it will work with any socket that you +pass it. You are responsible for opening and closing the socket. + +PEXPECT LICENSE + + This license is approved by the OSI and FSF as GPL-compatible. + http://opensource.org/licenses/isc-license.txt + + Copyright (c) 2012, Noah Spurrier <noah@noah.org> + PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY + PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE + COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +""" + +import socket +from contextlib import contextmanager + +from .exceptions import TIMEOUT, EOF +from .spawnbase import SpawnBase + +__all__ = ["SocketSpawn"] + + +class SocketSpawn(SpawnBase): + """This is like :mod:`pexpect.fdpexpect` but uses the cross-platform python socket api, + rather than the unix-specific file descriptor api. Thus, it works with + remote connections on both unix and windows.""" + + def __init__( + self, + socket: socket.socket, + args=None, + timeout=30, + maxread=2000, + searchwindowsize=None, + logfile=None, + encoding=None, + codec_errors="strict", + use_poll=False, + ): + """This takes an open socket.""" + + self.args = None + self.command = None + SpawnBase.__init__( + self, + timeout, + maxread, + searchwindowsize, + logfile, + encoding=encoding, + codec_errors=codec_errors, + ) + self.socket = socket + self.child_fd = socket.fileno() + self.closed = False + self.name = "<socket %s>" % socket + self.use_poll = use_poll + + def close(self): + """Close the socket. + + Calling this method a second time does nothing, but if the file + descriptor was closed elsewhere, :class:`OSError` will be raised. + """ + if self.child_fd == -1: + return + + self.flush() + self.socket.shutdown(socket.SHUT_RDWR) + self.socket.close() + self.child_fd = -1 + self.closed = True + + def isalive(self): + """ Alive if the fileno is valid """ + return self.socket.fileno() >= 0 + + def send(self, s) -> int: + """Write to socket, return number of bytes written""" + s = self._coerce_send_string(s) + self._log(s, "send") + + b = self._encoder.encode(s, final=False) + self.socket.sendall(b) + return len(b) + + def sendline(self, s) -> int: + """Write to socket with trailing newline, return number of bytes written""" + s = self._coerce_send_string(s) + return self.send(s + self.linesep) + + def write(self, s): + """Write to socket, return None""" + self.send(s) + + def writelines(self, sequence): + "Call self.write() for each item in sequence" + for s in sequence: + self.write(s) + + @contextmanager + def _timeout(self, timeout): + saved_timeout = self.socket.gettimeout() + try: + self.socket.settimeout(timeout) + yield + finally: + self.socket.settimeout(saved_timeout) + + def read_nonblocking(self, size=1, timeout=-1): + """ + Read from the file descriptor and return the result as a string. + + The read_nonblocking method of :class:`SpawnBase` assumes that a call + to os.read will not block (timeout parameter is ignored). This is not + the case for POSIX file-like objects such as sockets and serial ports. + + Use :func:`select.select`, timeout is implemented conditionally for + POSIX systems. + + :param int size: Read at most *size* bytes. + :param int timeout: Wait timeout seconds for file descriptor to be + ready to read. When -1 (default), use self.timeout. When 0, poll. + :return: String containing the bytes read + """ + if timeout == -1: + timeout = self.timeout + try: + with self._timeout(timeout): + s = self.socket.recv(size) + if s == b'': + self.flag_eof = True + raise EOF("Socket closed") + return s + except socket.timeout: + raise TIMEOUT("Timeout exceeded.") diff --git a/contrib/python/pexpect/py3/pexpect/spawnbase.py b/contrib/python/pexpect/py3/pexpect/spawnbase.py index 59e905764c..abf8071ec1 100644 --- a/contrib/python/pexpect/py3/pexpect/spawnbase.py +++ b/contrib/python/pexpect/py3/pexpect/spawnbase.py @@ -141,6 +141,16 @@ class SpawnBase(object): return s.encode('ascii') return s + # In bytes mode, regex patterns should also be of bytes type + def _coerce_expect_re(self, r): + p = r.pattern + if self.encoding is None and not isinstance(p, bytes): + return re.compile(p.encode('utf-8')) + # And vice-versa + elif self.encoding is not None and isinstance(p, bytes): + return re.compile(p.decode('utf-8')) + return r + def _coerce_send_string(self, s): if self.encoding is None and not isinstance(s, bytes): return s.encode('utf-8') @@ -153,7 +163,7 @@ class SpawnBase(object): self._buffer = self.buffer_type() self._buffer.write(value) - # This property is provided for backwards compatability (self.buffer used + # This property is provided for backwards compatibility (self.buffer used # to be a string/bytes object) buffer = property(_get_buffer, _set_buffer) @@ -235,6 +245,7 @@ class SpawnBase(object): elif p is TIMEOUT: compiled_pattern_list.append(TIMEOUT) elif isinstance(p, type(re.compile(''))): + p = self._coerce_expect_re(p) compiled_pattern_list.append(p) else: self._pattern_type_err(p) diff --git a/contrib/python/pexpect/py3/ya.make b/contrib/python/pexpect/py3/ya.make index 55ab18102d..0c1a022fac 100644 --- a/contrib/python/pexpect/py3/ya.make +++ b/contrib/python/pexpect/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(4.8.0) +VERSION(4.9.0) LICENSE(ISC) @@ -12,12 +12,18 @@ PEERDIR( NO_LINT() +NO_CHECK_IMPORTS( + pexpect._async_pre_await +) + PY_SRCS( TOP_LEVEL pexpect/ANSI.py pexpect/FSM.py pexpect/__init__.py pexpect/_async.py + pexpect/_async_pre_await.py + pexpect/_async_w_await.py pexpect/exceptions.py pexpect/expect.py pexpect/fdpexpect.py @@ -27,6 +33,7 @@ PY_SRCS( pexpect/replwrap.py pexpect/run.py pexpect/screen.py + pexpect/socket_pexpect.py pexpect/spawnbase.py pexpect/utils.py ) |