在 pexpect spawn 中使用 poll 而不是 select
use poll instead of select in pexpect spawn
我有以下测试代码,
import pexpect
import time
session = {}
try:
for i in range(1030):
print(i)
child = pexpect.spawn(cmd,encoding='utf-8')
child.expect("mgmt",200)
session[i]=child
print(child)
with open("command.txt","w") as fobj:
child.logfile_read=fobj
child.sendline ("server 0")
child.expect ("server0", 200)
with open("command.txt","r") as temp:
command_output=temp.read()
print(command_output)
time.sleep(5000)
except Exception as e:
print("mgmt launch failed")
print(e)
此代码打开超过 1024 个文件描述符并产生以下回溯,
server0>
1018
<pexpect.pty_spawn.spawn object at 0x7f5f8ddf6f28>
buffer (last 100 chars): '>'
after: 'mgmt'
match: <_sre.SRE_Match object; span=(452, 461), match='mgmt'>
match_index: 0
exitstatus: None
flag_eof: False
pid: 11126
child_fd: 1023
closed: False
timeout: 30
delimiter: <class 'pexpect.exceptions.EOF'>
logfile: None
logfile_read: None
logfile_send: None
maxread: 2000
ignorecase: False
searchwindowsize: None
delaybeforesend: 0.05
delayafterclose: 0.1
delayafterterminate: 0.1
server 0
server0>
1019
mgmt launch failed
filedescriptor out of range in select()
Traceback (most recent call last):
File "test1.py", line 9, in <module>
child.expect("mgmt",200)
File "/usr/lib/python3/dist-packages/pexpect/spawnbase.py", line 321, in expect
timeout, searchwindowsize, async)
File "/usr/lib/python3/dist-packages/pexpect/spawnbase.py", line 345, in expect_list
return exp.expect_loop(timeout)
File "/usr/lib/python3/dist-packages/pexpect/expect.py", line 99, in expect_loop
incoming = spawn.read_nonblocking(spawn.maxread, timeout)
File "/usr/lib/python3/dist-packages/pexpect/pty_spawn.py", line 452, in read_nonblocking
r, w, e = select_ignore_interrupts([self.child_fd], [], [], timeout)
File "/usr/lib/python3/dist-packages/pexpect/utils.py", line 138, in select_ignore_interrupts
return select.select(iwtd, owtd, ewtd, timeout)
ValueError: filedescriptor out of range in select()
我已经读到,为了克服这个问题,应该使用 poll() 而不是 select() 但找不到关于在使用 pexpect.spawn() 时如何使用 poll() 的示例.我怎样才能明确地说 Python 来使用 poll() 而不是 socket()?
pexpect
模块不支持这样做 out-of-the-box。但是,monkey-patch spawn
对象的 __select
方法并不是非常困难,这是系统 select
实际被调用的地方。
Monkey-patching 表示用您自己的版本替换对象在 run-time 的方法。这在 python 中很容易做到,如果 要替换的方法具有干净的界面。在这种情况下,它非常 straight-forward 因为 pexpect
已经将 select
功能隔离到这个方法,它有一个非常合乎逻辑和干净的界面。
实现类似于下面的代码。请注意,此处 my_select
函数的大部分是复制当前 __select
对 EINTR
的处理。另请注意,更通用的解决方案也可以正确处理 owtd
和 ewtd
参数。这里没有必要,因为这些参数总是作为我正在查看的 pexpect
模块中的空列表传递。最后警告:不提供保修:)。 None 已经过测试。
import select
import sys
import errno
def my_select(self, iwtd, owtd, ewtd, timeout=None):
if timeout is not None:
end_time = time.time() + timeout
poll_obj = select.poll()
for fd in iwtd:
poll_obj.register(fd, select.POLLIN | select.POLLPRI | select.POLLERR | select.POLLHUP)
while True:
poll_obj.poll(timeout)
try:
poll_fds = poll_obj.poll(timeout)
return ([fd for fd, _status in poll_fds], [], [])
except select.error:
err = sys.exc_info()[1]
if err.args[0] == errno.EINTR:
# if we loop back we have to subtract the
# amount of time we already waited.
if timeout is not None:
timeout = end_time - time.time()
if timeout < 0:
return([], [], [])
else:
# something else caused the select.error, so
# this actually is an exception.
raise
# Your main code...
child = pexpect.spawn(cmd,encoding='utf-8')
# Monkey-patch my_select method into place
child.__select = my_select
child.expect("mgmt",200)
...
monkey-patching 有缺点。如果模块的系统版本升级和重组,monkey-patch 可能不再有意义。因此,如果您对这种风险感到不安,您可以简单地将模块复制到您自己的源层次结构中(可能重命名它以避免混淆),然后直接对其 __select
方法进行相同的更改。
添加到响应中,因为预期 version 4.5 选项 use_poll
可以在创建 spawn
对象时指定:
https://pexpect.readthedocs.io/en/stable/api/pexpect.html#pexpect.spawn.init
引用:
The use_poll attribute enables using select.poll() over select.select() for socket handling. This is handy if your system could have > 1024 fds
你的情况:
child = pexpect.spawn(cmd, encoding='utf-8', use_poll=True)
我有以下测试代码,
import pexpect
import time
session = {}
try:
for i in range(1030):
print(i)
child = pexpect.spawn(cmd,encoding='utf-8')
child.expect("mgmt",200)
session[i]=child
print(child)
with open("command.txt","w") as fobj:
child.logfile_read=fobj
child.sendline ("server 0")
child.expect ("server0", 200)
with open("command.txt","r") as temp:
command_output=temp.read()
print(command_output)
time.sleep(5000)
except Exception as e:
print("mgmt launch failed")
print(e)
此代码打开超过 1024 个文件描述符并产生以下回溯,
server0>
1018
<pexpect.pty_spawn.spawn object at 0x7f5f8ddf6f28>
buffer (last 100 chars): '>'
after: 'mgmt'
match: <_sre.SRE_Match object; span=(452, 461), match='mgmt'>
match_index: 0
exitstatus: None
flag_eof: False
pid: 11126
child_fd: 1023
closed: False
timeout: 30
delimiter: <class 'pexpect.exceptions.EOF'>
logfile: None
logfile_read: None
logfile_send: None
maxread: 2000
ignorecase: False
searchwindowsize: None
delaybeforesend: 0.05
delayafterclose: 0.1
delayafterterminate: 0.1
server 0
server0>
1019
mgmt launch failed
filedescriptor out of range in select()
Traceback (most recent call last):
File "test1.py", line 9, in <module>
child.expect("mgmt",200)
File "/usr/lib/python3/dist-packages/pexpect/spawnbase.py", line 321, in expect
timeout, searchwindowsize, async)
File "/usr/lib/python3/dist-packages/pexpect/spawnbase.py", line 345, in expect_list
return exp.expect_loop(timeout)
File "/usr/lib/python3/dist-packages/pexpect/expect.py", line 99, in expect_loop
incoming = spawn.read_nonblocking(spawn.maxread, timeout)
File "/usr/lib/python3/dist-packages/pexpect/pty_spawn.py", line 452, in read_nonblocking
r, w, e = select_ignore_interrupts([self.child_fd], [], [], timeout)
File "/usr/lib/python3/dist-packages/pexpect/utils.py", line 138, in select_ignore_interrupts
return select.select(iwtd, owtd, ewtd, timeout)
ValueError: filedescriptor out of range in select()
我已经读到,为了克服这个问题,应该使用 poll() 而不是 select() 但找不到关于在使用 pexpect.spawn() 时如何使用 poll() 的示例.我怎样才能明确地说 Python 来使用 poll() 而不是 socket()?
pexpect
模块不支持这样做 out-of-the-box。但是,monkey-patch spawn
对象的 __select
方法并不是非常困难,这是系统 select
实际被调用的地方。
Monkey-patching 表示用您自己的版本替换对象在 run-time 的方法。这在 python 中很容易做到,如果 要替换的方法具有干净的界面。在这种情况下,它非常 straight-forward 因为 pexpect
已经将 select
功能隔离到这个方法,它有一个非常合乎逻辑和干净的界面。
实现类似于下面的代码。请注意,此处 my_select
函数的大部分是复制当前 __select
对 EINTR
的处理。另请注意,更通用的解决方案也可以正确处理 owtd
和 ewtd
参数。这里没有必要,因为这些参数总是作为我正在查看的 pexpect
模块中的空列表传递。最后警告:不提供保修:)。 None 已经过测试。
import select
import sys
import errno
def my_select(self, iwtd, owtd, ewtd, timeout=None):
if timeout is not None:
end_time = time.time() + timeout
poll_obj = select.poll()
for fd in iwtd:
poll_obj.register(fd, select.POLLIN | select.POLLPRI | select.POLLERR | select.POLLHUP)
while True:
poll_obj.poll(timeout)
try:
poll_fds = poll_obj.poll(timeout)
return ([fd for fd, _status in poll_fds], [], [])
except select.error:
err = sys.exc_info()[1]
if err.args[0] == errno.EINTR:
# if we loop back we have to subtract the
# amount of time we already waited.
if timeout is not None:
timeout = end_time - time.time()
if timeout < 0:
return([], [], [])
else:
# something else caused the select.error, so
# this actually is an exception.
raise
# Your main code...
child = pexpect.spawn(cmd,encoding='utf-8')
# Monkey-patch my_select method into place
child.__select = my_select
child.expect("mgmt",200)
...
monkey-patching 有缺点。如果模块的系统版本升级和重组,monkey-patch 可能不再有意义。因此,如果您对这种风险感到不安,您可以简单地将模块复制到您自己的源层次结构中(可能重命名它以避免混淆),然后直接对其 __select
方法进行相同的更改。
添加到响应中,因为预期 version 4.5 选项 use_poll
可以在创建 spawn
对象时指定:
https://pexpect.readthedocs.io/en/stable/api/pexpect.html#pexpect.spawn.init
引用:
The use_poll attribute enables using select.poll() over select.select() for socket handling. This is handy if your system could have > 1024 fds
你的情况:
child = pexpect.spawn(cmd, encoding='utf-8', use_poll=True)