summaryrefslogtreecommitdiffstats
path: root/contrib/python/pexpect
diff options
context:
space:
mode:
authormaxim-yurchuk <[email protected]>2024-10-09 12:29:46 +0300
committermaxim-yurchuk <[email protected]>2024-10-09 13:14:22 +0300
commit9731d8a4bb7ee2cc8554eaf133bb85498a4c7d80 (patch)
treea8fb3181d5947c0d78cf402aa56e686130179049 /contrib/python/pexpect
parenta44b779cd359f06c3ebbef4ec98c6b38609d9d85 (diff)
publishFullContrib: true for ydb
<HIDDEN_URL> commit_hash:c82a80ac4594723cebf2c7387dec9c60217f603e
Diffstat (limited to 'contrib/python/pexpect')
-rw-r--r--contrib/python/pexpect/py2/.yandex_meta/yamaker.yaml3
-rw-r--r--contrib/python/pexpect/py2/pexpect/_async.py28
-rw-r--r--contrib/python/pexpect/py2/pexpect/_async_pre_await.py111
-rw-r--r--contrib/python/pexpect/py2/pexpect/_async_w_await.py118
-rw-r--r--contrib/python/pexpect/py2/pexpect/socket_pexpect.py145
-rw-r--r--contrib/python/pexpect/py3/.yandex_meta/yamaker.yaml2
6 files changed, 407 insertions, 0 deletions
diff --git a/contrib/python/pexpect/py2/.yandex_meta/yamaker.yaml b/contrib/python/pexpect/py2/.yandex_meta/yamaker.yaml
new file mode 100644
index 00000000000..44fce9cb7c2
--- /dev/null
+++ b/contrib/python/pexpect/py2/.yandex_meta/yamaker.yaml
@@ -0,0 +1,3 @@
+mark_as_py3_sources:
+ - pexpect/_async*.py
+ - pexpect/socket_pexpect.py
diff --git a/contrib/python/pexpect/py2/pexpect/_async.py b/contrib/python/pexpect/py2/pexpect/_async.py
new file mode 100644
index 00000000000..261720c1606
--- /dev/null
+++ b/contrib/python/pexpect/py2/pexpect/_async.py
@@ -0,0 +1,28 @@
+"""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/py2/pexpect/_async_pre_await.py b/contrib/python/pexpect/py2/pexpect/_async_pre_await.py
new file mode 100644
index 00000000000..81ece1b6da1
--- /dev/null
+++ b/contrib/python/pexpect/py2/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
+
+
+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)
+
+
+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/py2/pexpect/_async_w_await.py b/contrib/python/pexpect/py2/pexpect/_async_w_await.py
new file mode 100644
index 00000000000..59cb1ef13d5
--- /dev/null
+++ b/contrib/python/pexpect/py2/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/py2/pexpect/socket_pexpect.py b/contrib/python/pexpect/py2/pexpect/socket_pexpect.py
new file mode 100644
index 00000000000..cb11ac22589
--- /dev/null
+++ b/contrib/python/pexpect/py2/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 <[email protected]>
+ 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/.yandex_meta/yamaker.yaml b/contrib/python/pexpect/py3/.yandex_meta/yamaker.yaml
new file mode 100644
index 00000000000..611e6379dba
--- /dev/null
+++ b/contrib/python/pexpect/py3/.yandex_meta/yamaker.yaml
@@ -0,0 +1,2 @@
+mark_as_py3_sources:
+ - pexpect/_async.py