1 | """ |
---|
2 | Pexpect is a Python module for spawning child applications; |
---|
3 | controlling them; and responding to expected patterns in their output. |
---|
4 | Pexpect can be used for automating interactive applications such as |
---|
5 | ssh, ftp, passwd, telnet, etc. It can be used to a automate setup scripts |
---|
6 | for duplicating software package installations on different servers. |
---|
7 | It can be used for automated software testing. Pexpect is in the spirit of |
---|
8 | Don Libes' Expect, but Pexpect is pure Python. Other Expect-like |
---|
9 | modules for Python require TCL and Expect or require C extensions to |
---|
10 | be compiled. Pexpect does not use C, Expect, or TCL extensions. It |
---|
11 | should work on any platform that supports the standard Python pty |
---|
12 | module. The Pexpect interface focuses on ease of use so that simple |
---|
13 | tasks are easy. |
---|
14 | |
---|
15 | Pexpect is Open Source, Free, and all Good that stuff. |
---|
16 | License: Python Software Foundation License |
---|
17 | http://www.opensource.org/licenses/PythonSoftFoundation.html |
---|
18 | |
---|
19 | Noah Spurrier |
---|
20 | |
---|
21 | $Revision: 966 $ |
---|
22 | $Date: 2006-07-12 18:27:02 +0000 (Qua, 12 Jul 2006) $ |
---|
23 | """ |
---|
24 | |
---|
25 | |
---|
26 | try: |
---|
27 | import os, sys |
---|
28 | import select |
---|
29 | import string |
---|
30 | import re |
---|
31 | import struct |
---|
32 | import resource |
---|
33 | from types import * |
---|
34 | import pty |
---|
35 | import tty |
---|
36 | import termios |
---|
37 | import fcntl |
---|
38 | except ImportError, e: |
---|
39 | raise ImportError, str(e) + """ |
---|
40 | A critical module was not found. Probably this OS does not support it. |
---|
41 | Currently pexpect is intended for UNIX operating systems.""" |
---|
42 | |
---|
43 | |
---|
44 | |
---|
45 | __version__ = '0.99' |
---|
46 | __revision__ = '$Revision: 966 $' |
---|
47 | __all__ = ['ExceptionPexpect', 'EOF', 'TIMEOUT', 'spawn', 'run', |
---|
48 | '__version__', '__revision__'] |
---|
49 | |
---|
50 | |
---|
51 | |
---|
52 | # Exception classes used by this module. |
---|
53 | class ExceptionPexpect(Exception): |
---|
54 | """Base class for all exceptions raised by this module.""" |
---|
55 | def __init__(self, value): |
---|
56 | self.value = value |
---|
57 | def __str__(self): |
---|
58 | return `self.value` |
---|
59 | class EOF(ExceptionPexpect): |
---|
60 | """Raised when EOF is read from a child.""" |
---|
61 | class TIMEOUT(ExceptionPexpect): |
---|
62 | """Raised when a read time exceeds the timeout.""" |
---|
63 | ##class MAXBUFFER(ExceptionPexpect): |
---|
64 | ## """Raised when a scan buffer fills before matching an expected pattern.""" |
---|
65 | |
---|
66 | |
---|
67 | |
---|
68 | def run (command, args=[], timeout=30): |
---|
69 | """This runs a command; waits for it to finish; then returns |
---|
70 | all output as a string. This is a utility interface around |
---|
71 | the spawn class. |
---|
72 | """ |
---|
73 | child = spawn(command, args, timeout) |
---|
74 | child.expect (EOF) |
---|
75 | return child.before |
---|
76 | |
---|
77 | class spawn: |
---|
78 | """This is the main class interface for Pexpect. Use this class to |
---|
79 | start and control child applications. |
---|
80 | """ |
---|
81 | |
---|
82 | def __init__(self, command, args=[], timeout=30): |
---|
83 | """This is the constructor. The command parameter may be a string |
---|
84 | that includes a command and any arguments to the command. For example: |
---|
85 | p = pexpect.spawn ('/usr/bin/ftp') |
---|
86 | p = pexpect.spawn ('/usr/bin/ssh user@example.com') |
---|
87 | p = pexpect.spawn ('ls -latr /tmp') |
---|
88 | You may also construct it with a list of arguments like so: |
---|
89 | p = pexpect.spawn ('/usr/bin/ftp', []) |
---|
90 | p = pexpect.spawn ('/usr/bin/ssh', ['user@example.com']) |
---|
91 | p = pexpect.spawn ('ls', ['-latr', '/tmp']) |
---|
92 | After this the child application will be created and |
---|
93 | will be ready to talk to. For normal use, see expect() and |
---|
94 | send() and sendline(). |
---|
95 | |
---|
96 | If the command parameter is an integer AND a valid file descriptor |
---|
97 | then spawn will talk to the file descriptor instead. This can be |
---|
98 | used to act expect features to any file descriptor. For example: |
---|
99 | fd = os.open ('somefile.txt', os.O_RDONLY) |
---|
100 | s = pexpect.spawn (fd) |
---|
101 | The original creator of the file descriptor is responsible |
---|
102 | for closing it. Spawn will not try to close it and spawn will |
---|
103 | raise an exception if you try to call spawn.close(). |
---|
104 | """ |
---|
105 | |
---|
106 | self.STDIN_FILENO = sys.stdin.fileno() |
---|
107 | self.STDOUT_FILENO = sys.stdout.fileno() |
---|
108 | self.STDERR_FILENO = sys.stderr.fileno() |
---|
109 | |
---|
110 | self.stdin = sys.stdin |
---|
111 | self.stdout = sys.stdout |
---|
112 | self.stderr = sys.stderr |
---|
113 | |
---|
114 | self.timeout = timeout |
---|
115 | self.child_fd = -1 # initially closed |
---|
116 | self.__child_fd_owner = None |
---|
117 | self.exitstatus = None |
---|
118 | self.pid = None |
---|
119 | self.log_file = None |
---|
120 | self.before = None |
---|
121 | self.after = None |
---|
122 | self.match = None |
---|
123 | self.softspace = 0 # File-like object. |
---|
124 | self.name = '' # File-like object. |
---|
125 | self.flag_eof = 0 |
---|
126 | |
---|
127 | # NEW -- to support buffering -- the ability to read more than one |
---|
128 | # byte from a TTY at a time. See setmaxread() method. |
---|
129 | self.buffer = '' |
---|
130 | self.maxread = 1 # Maximum to read at a time |
---|
131 | ### IMPLEMENT THIS FEATURE!!! |
---|
132 | # anything before maxsearchsize point is preserved, but not searched. |
---|
133 | #self.maxsearchsize = 1000 |
---|
134 | |
---|
135 | # If command is an int type then it must represent an open file descriptor. |
---|
136 | if type (command) == type(0): |
---|
137 | try: # Command is an int, so now check if it is a file descriptor. |
---|
138 | os.fstat(command) |
---|
139 | except OSError: |
---|
140 | raise ExceptionPexpect, 'Command is an int type, yet is not a valid file descriptor.' |
---|
141 | self.pid = -1 |
---|
142 | self.child_fd = command |
---|
143 | self.__child_fd_owner = 0 # Sets who is reponsible for the child_fd |
---|
144 | self.args = None |
---|
145 | self.command = None |
---|
146 | self.name = '<file descriptor>' |
---|
147 | return |
---|
148 | |
---|
149 | if type (args) != type([]): |
---|
150 | raise TypeError, 'The second argument, args, must be a list.' |
---|
151 | |
---|
152 | if args == []: |
---|
153 | self.args = _split_command_line(command) |
---|
154 | self.command = self.args[0] |
---|
155 | else: |
---|
156 | self.args = args |
---|
157 | self.args.insert (0, command) |
---|
158 | self.command = command |
---|
159 | self.name = '<' + reduce(lambda x, y: x+' '+y, self.args) + '>' |
---|
160 | |
---|
161 | self.__spawn() |
---|
162 | |
---|
163 | def __del__(self): |
---|
164 | """This makes sure that no system resources are left open. |
---|
165 | Python only garbage collects Python objects. OS file descriptors |
---|
166 | are not Python objects, so they must be handled explicitly. |
---|
167 | If the child file descriptor was opened outside of this class |
---|
168 | (passed to the constructor) then this does not close it. |
---|
169 | """ |
---|
170 | try: # CNC:2003-09-23 |
---|
171 | if self.__child_fd_owner: |
---|
172 | self.close() |
---|
173 | except OSError: |
---|
174 | pass |
---|
175 | |
---|
176 | def __spawn(self): |
---|
177 | """This starts the given command in a child process. This does |
---|
178 | all the fork/exec type of stuff for a pty. This is called by |
---|
179 | __init__. The args parameter is a list, command is a string. |
---|
180 | """ |
---|
181 | # The pid and child_fd of this object get set by this method. |
---|
182 | # Note that it is difficult for this method to fail. |
---|
183 | # You cannot detect if the child process cannot start. |
---|
184 | # So the only way you can tell if the child process started |
---|
185 | # or not is to try to read from the file descriptor. If you get |
---|
186 | # EOF immediately then it means that the child is already dead. |
---|
187 | # That may not necessarily be bad, because you may haved spawned a child |
---|
188 | # that performs some task; creates no stdout output; and then dies. |
---|
189 | # It is a fuzzy edge case. Any child process that you are likely to |
---|
190 | # want to interact with Pexpect would probably not fall into this |
---|
191 | # category. |
---|
192 | # FYI, This essentially does a fork/exec operation. |
---|
193 | |
---|
194 | assert self.pid == None, 'The pid member is not None.' |
---|
195 | assert self.command != None, 'The command member is None.' |
---|
196 | |
---|
197 | if _which(self.command) == None: |
---|
198 | raise ExceptionPexpect ('The command was not found or was not executable: %s.' % self.command) |
---|
199 | |
---|
200 | try: |
---|
201 | self.pid, self.child_fd = pty.fork() |
---|
202 | except OSError, e: |
---|
203 | raise ExceptionPexpect('Pexpect: pty.fork() failed: ' + str(e)) |
---|
204 | |
---|
205 | if self.pid == 0: # Child |
---|
206 | try: # Some platforms do not like setwinsize (Cygwin). |
---|
207 | self.child_fd = sys.stdout.fileno() |
---|
208 | self.setwinsize(24, 80) |
---|
209 | except: |
---|
210 | pass |
---|
211 | # Do not allow child to inherit open file descriptors from parent. |
---|
212 | max_fd = resource.getrlimit(resource.RLIMIT_NOFILE)[0] |
---|
213 | for i in range (3, max_fd): |
---|
214 | try: |
---|
215 | os.close (i) |
---|
216 | except OSError: |
---|
217 | pass |
---|
218 | |
---|
219 | os.environ["LANG"] = "C" |
---|
220 | os.environ["LANGUAGE"] = "C" |
---|
221 | os.environ["LC_ALL"] = "C" |
---|
222 | os.execvp(self.command, self.args) |
---|
223 | |
---|
224 | # Parent |
---|
225 | self.__child_fd_owner = 1 |
---|
226 | |
---|
227 | def fileno (self): # File-like object. |
---|
228 | """This returns the file descriptor of the pty for the child.""" |
---|
229 | return self.child_fd |
---|
230 | |
---|
231 | def close (self, wait=1): # File-like object. |
---|
232 | """ This closes the connection with the child application. |
---|
233 | It makes no attempt to actually kill the child or wait for its status. |
---|
234 | If the file descriptor was set by passing a file descriptor |
---|
235 | to the constructor then this method raises an exception. |
---|
236 | Note that calling close() more than once is valid. |
---|
237 | This emulates standard Python behavior with files. |
---|
238 | If wait is set to True then close will wait |
---|
239 | for the exit status of the process. Doing a wait is a blocking call, |
---|
240 | but this usually takes almost no time at all. Generally, |
---|
241 | you don't have to worry about this. If you are |
---|
242 | creating lots of children then you usually want to call wait. |
---|
243 | Only set wait to false if you know the child will |
---|
244 | continue to run after closing the controlling TTY. |
---|
245 | Otherwise you will end up with defunct (zombie) processes. |
---|
246 | """ |
---|
247 | if self.child_fd != -1: |
---|
248 | if not self.__child_fd_owner: |
---|
249 | raise ExceptionPexpect ('This file descriptor cannot be closed because it was not created by spawn. The original creator is responsible for closing it.') |
---|
250 | self.flush() |
---|
251 | os.close (self.child_fd) |
---|
252 | if wait: |
---|
253 | os.waitpid (self.pid, 0) |
---|
254 | self.child_fd = -1 |
---|
255 | self.__child_fd_owner = None |
---|
256 | |
---|
257 | def flush (self): # File-like object. |
---|
258 | """This does nothing. It is here to support the interface for a File-like object. |
---|
259 | """ |
---|
260 | pass |
---|
261 | |
---|
262 | def isatty (self): # File-like object. |
---|
263 | """This returns 1 if the file descriptor is open and |
---|
264 | connected to a tty(-like) device, else 0. |
---|
265 | """ |
---|
266 | return os.isatty(self.child_fd) |
---|
267 | |
---|
268 | def setecho (self, on): |
---|
269 | """This sets the terminal echo mode on or off.""" |
---|
270 | new = termios.tcgetattr(self.child_fd) |
---|
271 | if on: |
---|
272 | new[3] = new[3] | termios.ECHO # lflags |
---|
273 | else: |
---|
274 | new[3] = new[3] & ~termios.ECHO # lflags |
---|
275 | termios.tcsetattr(self.child_fd, termios.TCSADRAIN, new) |
---|
276 | |
---|
277 | def setlog (self, fileobject): |
---|
278 | """This sets logging output to go to the given fileobject. |
---|
279 | Set fileobject to None to stop logging. |
---|
280 | Example: |
---|
281 | child = pexpect.spawn('some_command') |
---|
282 | fout = file('mylog.txt','w') |
---|
283 | child.setlog (fout) |
---|
284 | ... |
---|
285 | """ |
---|
286 | self.log_file = fileobject |
---|
287 | |
---|
288 | def setmaxread (self, maxread): |
---|
289 | """This sets the maximum number of bytes to read from a TTY at one time. |
---|
290 | This is used to change the read buffer size. When a pexpect.spawn |
---|
291 | object is created the default maxread is 1 (unbuffered). |
---|
292 | Set this value higher to turn on buffer. This should help performance |
---|
293 | in cases where large amounts of output are read back from the child. |
---|
294 | """ |
---|
295 | self.maxread = maxread |
---|
296 | |
---|
297 | def read_nonblocking (self, size = 1, timeout = None): |
---|
298 | """ |
---|
299 | This reads at most size characters from the child application. |
---|
300 | It includes a timeout. If the read does not complete within the |
---|
301 | timeout period then a TIMEOUT exception is raised. |
---|
302 | If the end of file is read then an EOF exception will be raised. |
---|
303 | If a log file was set using setlog() then all data will |
---|
304 | also be written to the log file. |
---|
305 | |
---|
306 | Notice that if this method is called with timeout=None |
---|
307 | then it actually may block. |
---|
308 | |
---|
309 | This is a non-blocking wrapper around os.read(). |
---|
310 | It uses select.select() to implement a timeout. |
---|
311 | """ |
---|
312 | |
---|
313 | if self.child_fd == -1: |
---|
314 | raise ValueError ('I/O operation on closed file') |
---|
315 | |
---|
316 | # Note that some systems like Solaris don't seem to ever give |
---|
317 | # an EOF when the child dies. In fact, you can still try to read |
---|
318 | # from the child_fd -- it will block forever or until TIMEOUT. |
---|
319 | # For this case, I test isalive() before doing any reading. |
---|
320 | # If isalive() is false, then I pretend that this is the same as EOF. |
---|
321 | if not self.isalive(): |
---|
322 | r, w, e = select.select([self.child_fd], [], [], 0) |
---|
323 | if not r: |
---|
324 | self.flag_eof = 1 |
---|
325 | raise EOF ('End Of File (EOF) in read(). Braindead platform.') |
---|
326 | |
---|
327 | r, w, e = select.select([self.child_fd], [], [], timeout) |
---|
328 | if not r: |
---|
329 | raise TIMEOUT('Timeout exceeded in read().') |
---|
330 | |
---|
331 | if self.child_fd in r: |
---|
332 | try: |
---|
333 | s = os.read(self.child_fd, size) |
---|
334 | except OSError, e: |
---|
335 | self.flag_eof = 1 |
---|
336 | raise EOF('End Of File (EOF) in read(). Exception style platform.') |
---|
337 | if s == '': |
---|
338 | self.flag_eof = 1 |
---|
339 | raise EOF('End Of File (EOF) in read(). Empty string style platform.') |
---|
340 | |
---|
341 | if self.log_file != None: |
---|
342 | self.log_file.write (s) |
---|
343 | self.log_file.flush() |
---|
344 | |
---|
345 | return s |
---|
346 | |
---|
347 | raise ExceptionPexpect('Reached an unexpected state in read().') |
---|
348 | |
---|
349 | def read (self, size = -1): # File-like object. |
---|
350 | """This reads at most size bytes from the file |
---|
351 | (less if the read hits EOF before obtaining size bytes). |
---|
352 | If the size argument is negative or omitted, |
---|
353 | read all data until EOF is reached. |
---|
354 | The bytes are returned as a string object. |
---|
355 | An empty string is returned when EOF is encountered immediately. |
---|
356 | """ |
---|
357 | if size == 0: |
---|
358 | return '' |
---|
359 | if size < 0: |
---|
360 | self.expect (EOF) |
---|
361 | return self.before |
---|
362 | |
---|
363 | # I could have done this more directly by not using expect(), but |
---|
364 | # I deliberately decided to couple read() to expect() so that |
---|
365 | # I would catch any bugs early and ensure consistant behavior. |
---|
366 | # It's a little less efficient, but there is less for me to |
---|
367 | # worry about if I have to later modify read() or expect(). |
---|
368 | cre = re.compile('.{%d}' % size, re.DOTALL) |
---|
369 | index = self.expect ([cre, EOF]) |
---|
370 | if index == 0: |
---|
371 | return self.after ### self.before should be ''. Should I assert this? |
---|
372 | return self.before |
---|
373 | |
---|
374 | def readline (self, size = -1): # File-like object. |
---|
375 | """This reads and returns one entire line. A trailing newline is kept in |
---|
376 | the string, but may be absent when a file ends with an incomplete line. |
---|
377 | Note: This readline() looks for a \r\n pair even on UNIX because this is |
---|
378 | what the pseudo tty device returns. So contrary to what you may be used to |
---|
379 | you will get a newline as \r\n. |
---|
380 | An empty string is returned when EOF is hit immediately. |
---|
381 | Currently, the size agument is mostly ignored, so this behavior is not |
---|
382 | standard for a file-like object. |
---|
383 | """ |
---|
384 | if size == 0: |
---|
385 | return '' |
---|
386 | index = self.expect (['\r\n', EOF]) |
---|
387 | if index == 0: |
---|
388 | return self.before + '\r\n' |
---|
389 | else: |
---|
390 | return self.before |
---|
391 | |
---|
392 | def __iter__ (self): |
---|
393 | """This is to support interators over a file-like object. |
---|
394 | """ |
---|
395 | return self |
---|
396 | def next (self): |
---|
397 | """This is to support iterators over a file-like object. |
---|
398 | """ |
---|
399 | result = self.readline() |
---|
400 | if result == "": |
---|
401 | raise StopIteration |
---|
402 | return result |
---|
403 | |
---|
404 | def readlines (self, sizehint = -1): # File-like object. |
---|
405 | """This reads until EOF using readline() and returns a list containing |
---|
406 | the lines thus read. The optional sizehint argument is ignored. |
---|
407 | """ |
---|
408 | lines = [] |
---|
409 | while 1: |
---|
410 | line = self.readline() |
---|
411 | if not line: |
---|
412 | break |
---|
413 | lines.append(line) |
---|
414 | return lines |
---|
415 | |
---|
416 | def write(self, str): # File-like object. |
---|
417 | """This is similar to send() except that there is no return value. |
---|
418 | """ |
---|
419 | self.send (str) |
---|
420 | |
---|
421 | def writelines (self, sequence): # File-like object. |
---|
422 | """This calls write() for each element in the sequence. |
---|
423 | The sequence can be any iterable object producing strings, |
---|
424 | typically a list of strings. This does not add line separators |
---|
425 | There is no return value. |
---|
426 | """ |
---|
427 | for str in sequence: |
---|
428 | self.write (str) |
---|
429 | |
---|
430 | def send(self, str): |
---|
431 | """This sends a string to the child process. |
---|
432 | This returns the number of bytes written. |
---|
433 | """ |
---|
434 | return os.write(self.child_fd, str) |
---|
435 | |
---|
436 | def sendline(self, str=''): |
---|
437 | """This is like send(), but it adds a line feed (os.linesep). |
---|
438 | This returns the number of bytes written. |
---|
439 | """ |
---|
440 | n = self.send(str) |
---|
441 | n = n + self.send (os.linesep) |
---|
442 | return n |
---|
443 | |
---|
444 | def sendeof(self): |
---|
445 | """This sends an EOF to the child. |
---|
446 | This sends a character which causes the pending parent output |
---|
447 | buffer to be sent to the waiting child program without |
---|
448 | waiting for end-of-line. If it is the first character of the |
---|
449 | line, the read() in the user program returns 0, which |
---|
450 | signifies end-of-file. This means to work as expected |
---|
451 | a sendeof() has to be called at the begining of a line. |
---|
452 | This method does not send a newline. It is the responsibility |
---|
453 | of the caller to ensure the eof is sent at the beginning of a line. |
---|
454 | """ |
---|
455 | ### Hmmm... how do I send an EOF? |
---|
456 | ###C if ((m = write(pty, *buf, p - *buf)) < 0) |
---|
457 | ###C return (errno == EWOULDBLOCK) ? n : -1; |
---|
458 | fd = sys.stdin.fileno() |
---|
459 | old = termios.tcgetattr(fd) # remember current state |
---|
460 | new = termios.tcgetattr(fd) |
---|
461 | new[3] = new[3] | termios.ICANON # lflags |
---|
462 | # use try/finally to ensure state gets restored |
---|
463 | try: |
---|
464 | # EOF is recognized when ICANON is set, so make sure it is set. |
---|
465 | termios.tcsetattr(fd, termios.TCSADRAIN, new) |
---|
466 | os.write (self.child_fd, '%c' % termios.CEOF) |
---|
467 | finally: |
---|
468 | termios.tcsetattr(fd, termios.TCSADRAIN, old) # restore state |
---|
469 | |
---|
470 | def eof (self): |
---|
471 | """This returns 1 if the EOF exception was raised at some point. |
---|
472 | """ |
---|
473 | return self.flag_eof |
---|
474 | |
---|
475 | def isalive(self): |
---|
476 | """This tests if the child process is running or not. |
---|
477 | This returns 1 if the child process appears to be running or 0 if not. |
---|
478 | This also sets the exitstatus attribute. |
---|
479 | It can take literally SECONDS for Solaris to return the right status. |
---|
480 | This is the most wiggly part of Pexpect, but I think I've almost got |
---|
481 | it nailed down. |
---|
482 | """ |
---|
483 | # I can't use signals. Signals on UNIX suck and they |
---|
484 | # mess up Python pipes (setting SIGCHLD to SIGIGNORE). |
---|
485 | |
---|
486 | # If this class was created from an existing file descriptor then |
---|
487 | # I just check to see if the file descriptor is still valid. |
---|
488 | if self.pid == -1 and not self.__child_fd_owner: |
---|
489 | try: |
---|
490 | os.fstat(self.child_fd) |
---|
491 | return 1 |
---|
492 | except: |
---|
493 | return 0 |
---|
494 | |
---|
495 | try: |
---|
496 | pid, status = os.waitpid(self.pid, os.WNOHANG) |
---|
497 | except OSError: |
---|
498 | return 0 |
---|
499 | |
---|
500 | # I have to do this twice for Solaris. |
---|
501 | # I can't even believe that I figured this out... |
---|
502 | if pid == 0 and status == 0: |
---|
503 | try: |
---|
504 | pid, status = os.waitpid(self.pid, os.WNOHANG) |
---|
505 | #print 'Solaris sucks' |
---|
506 | except OSError: # This is crufty. When does this happen? |
---|
507 | return 0 |
---|
508 | # If pid and status is still 0 after two calls to waitpid() then |
---|
509 | # the process really is alive. This seems to work on all platforms. |
---|
510 | if pid == 0 and status == 0: |
---|
511 | return 1 |
---|
512 | |
---|
513 | # I do not OR this together because I want hooks for debugging. |
---|
514 | if os.WIFEXITED (status): |
---|
515 | self.exitstatus = os.WEXITSTATUS(status) |
---|
516 | return 0 |
---|
517 | elif os.WIFSTOPPED (status): |
---|
518 | return 0 |
---|
519 | elif os.WIFSIGNALED (status): |
---|
520 | return 0 |
---|
521 | else: |
---|
522 | return 0 # Can I ever get here? |
---|
523 | |
---|
524 | def kill(self, sig): |
---|
525 | """This sends the given signal to the child application. |
---|
526 | In keeping with UNIX tradition it has a misleading name. |
---|
527 | It does not necessarily kill the child unless |
---|
528 | you send the right signal. |
---|
529 | """ |
---|
530 | # Same as os.kill, but the pid is given for you. |
---|
531 | if self.isalive(): |
---|
532 | os.kill(self.pid, sig) |
---|
533 | |
---|
534 | def compile_pattern_list(self, patterns): |
---|
535 | """This compiles a pattern-string or a list of pattern-strings. |
---|
536 | Patterns must be a StringType, EOF, TIMEOUT, SRE_Pattern, or |
---|
537 | a list of those. |
---|
538 | |
---|
539 | This is used by expect() when calling expect_list(). |
---|
540 | Thus expect() is nothing more than:: |
---|
541 | cpl = self.compile_pattern_list(pl) |
---|
542 | return self.expect_list(clp, timeout) |
---|
543 | |
---|
544 | If you are using expect() within a loop it may be more |
---|
545 | efficient to compile the patterns first and then call expect_list(). |
---|
546 | This avoid calls in a loop to compile_pattern_list(): |
---|
547 | cpl = self.compile_pattern_list(my_pattern) |
---|
548 | while some_condition: |
---|
549 | ... |
---|
550 | i = self.expect_list(clp, timeout) |
---|
551 | ... |
---|
552 | """ |
---|
553 | if type(patterns) is not ListType: |
---|
554 | patterns = [patterns] |
---|
555 | |
---|
556 | compiled_pattern_list = [] |
---|
557 | for p in patterns: |
---|
558 | if type(p) is StringType: |
---|
559 | compiled_pattern_list.append(re.compile(p, re.DOTALL)) |
---|
560 | elif p is EOF: |
---|
561 | compiled_pattern_list.append(EOF) |
---|
562 | elif p is TIMEOUT: |
---|
563 | compiled_pattern_list.append(TIMEOUT) |
---|
564 | elif type(p) is type(re.compile('')): |
---|
565 | compiled_pattern_list.append(p) |
---|
566 | else: |
---|
567 | raise TypeError, 'Argument must be one of StringType, EOF, TIMEOUT, SRE_Pattern, or a list of those type. %s' % str(type(p)) |
---|
568 | |
---|
569 | return compiled_pattern_list |
---|
570 | |
---|
571 | def expect(self, pattern, timeout = -1): |
---|
572 | """This seeks through the stream until a pattern is matched. |
---|
573 | The pattern is overloaded and may take several types including a list. |
---|
574 | The pattern can be a StringType, EOF, a compiled re, or |
---|
575 | a list of those types. Strings will be compiled to re types. |
---|
576 | This returns the index into the pattern list. If the pattern was |
---|
577 | not a list this returns index 0 on a successful match. |
---|
578 | This may raise exceptions for EOF or TIMEOUT. |
---|
579 | To avoid the EOF or TIMEOUT exceptions add EOF or TIMEOUT to |
---|
580 | the pattern list. |
---|
581 | |
---|
582 | After a match is found the instance attributes |
---|
583 | 'before', 'after' and 'match' will be set. |
---|
584 | You can see all the data read before the match in 'before'. |
---|
585 | You can see the data that was matched in 'after'. |
---|
586 | The re.MatchObject used in the re match will be in 'match'. |
---|
587 | If an error occured then 'before' will be set to all the |
---|
588 | data read so far and 'after' and 'match' will be None. |
---|
589 | |
---|
590 | If timeout is -1 then timeout will be set to the self.timeout value. |
---|
591 | |
---|
592 | Note: A list entry may be EOF or TIMEOUT instead of a string. |
---|
593 | This will catch these exceptions and return the index |
---|
594 | of the list entry instead of raising the exception. |
---|
595 | The attribute 'after' will be set to the exception type. |
---|
596 | The attribute 'match' will be None. |
---|
597 | This allows you to write code like this: |
---|
598 | index = p.expect (['good', 'bad', pexpect.EOF, pexpect.TIMEOUT]) |
---|
599 | if index == 0: |
---|
600 | do_something() |
---|
601 | elif index == 1: |
---|
602 | do_something_else() |
---|
603 | elif index == 2: |
---|
604 | do_some_other_thing() |
---|
605 | elif index == 3: |
---|
606 | do_something_completely_different() |
---|
607 | instead of code like this: |
---|
608 | try: |
---|
609 | index = p.expect (['good', 'bad']) |
---|
610 | if index == 0: |
---|
611 | do_something() |
---|
612 | elif index == 1: |
---|
613 | do_something_else() |
---|
614 | except EOF: |
---|
615 | do_some_other_thing() |
---|
616 | except TIMEOUT: |
---|
617 | do_something_completely_different() |
---|
618 | These two forms are equivalent. It all depends on what you want. |
---|
619 | You can also just expect the EOF if you are waiting for all output |
---|
620 | of a child to finish. For example: |
---|
621 | p = pexpect.spawn('/bin/ls') |
---|
622 | p.expect (pexpect.EOF) |
---|
623 | print p.before |
---|
624 | """ |
---|
625 | compiled_pattern_list = self.compile_pattern_list(pattern) |
---|
626 | return self.expect_list(compiled_pattern_list, timeout) |
---|
627 | |
---|
628 | def expect_exact (self, pattern_list, timeout = -1): |
---|
629 | """This is similar to expect() except that it takes |
---|
630 | list of plain strings instead of regular expressions. |
---|
631 | The idea is that this should be much faster. It could also be |
---|
632 | useful when you don't want to have to worry about escaping |
---|
633 | regular expression characters that you want to match. |
---|
634 | You may also pass just a string without a list and the single |
---|
635 | string will be converted to a list. |
---|
636 | If timeout is -1 then timeout will be set to the self.timeout value. |
---|
637 | """ |
---|
638 | ### This is dumb. It shares most of the code with expect_list. |
---|
639 | ### The only different is the comparison method and that |
---|
640 | ### self.match is always None after calling this. |
---|
641 | if timeout == -1: |
---|
642 | timeout = self.timeout |
---|
643 | |
---|
644 | if type(pattern_list) is StringType: |
---|
645 | pattern_list = [pattern_list] |
---|
646 | |
---|
647 | try: |
---|
648 | #ED# incoming = '' |
---|
649 | incoming = self.buffer |
---|
650 | while 1: # Keep reading until exception or return. |
---|
651 | #ED# c = self.read_nonblocking (1, timeout) |
---|
652 | #ED# incoming = incoming + c |
---|
653 | |
---|
654 | # Sequence through the list of patterns and look for a match. |
---|
655 | index = -1 |
---|
656 | for str_target in pattern_list: |
---|
657 | index = index + 1 |
---|
658 | if str_target is EOF or str_target is TIMEOUT: |
---|
659 | continue # The Exception patterns are handled differently. |
---|
660 | match_index = incoming.find (str_target) |
---|
661 | if match_index >= 0: |
---|
662 | self.before = incoming [ : match_index] |
---|
663 | self.after = incoming [match_index : ] |
---|
664 | self.buffer = incoming [match_index + len(str_target):] |
---|
665 | self.match = None |
---|
666 | return index |
---|
667 | c = self.read_nonblocking (self.maxread, timeout) |
---|
668 | incoming = incoming + c |
---|
669 | |
---|
670 | except EOF: |
---|
671 | if EOF in pattern_list: |
---|
672 | self.before = incoming |
---|
673 | self.after = EOF |
---|
674 | self.buffer = '' |
---|
675 | return pattern_list.index(EOF) |
---|
676 | else: |
---|
677 | raise |
---|
678 | except TIMEOUT: |
---|
679 | if TIMEOUT in pattern_list: |
---|
680 | self.before = incoming |
---|
681 | self.after = TIMEOUT |
---|
682 | self.buffer = '' |
---|
683 | return pattern_list.index(TIMEOUT) |
---|
684 | else: |
---|
685 | raise |
---|
686 | except Exception: |
---|
687 | self.before = incoming |
---|
688 | self.after = None |
---|
689 | self.match = None |
---|
690 | self.buffer = '' |
---|
691 | raise |
---|
692 | |
---|
693 | def expect_list(self, pattern_list, timeout = -1): |
---|
694 | """ |
---|
695 | This takes a list of compiled regular expressions and returns |
---|
696 | the index into the pattern_list that matched the child's output. |
---|
697 | This is called by expect(). It is similar to the expect() method |
---|
698 | except that expect_list() is not overloaded. You must not pass |
---|
699 | anything except a list of compiled regular expressions. |
---|
700 | If timeout is -1 then timeout will be set to the self.timeout value. |
---|
701 | """ |
---|
702 | |
---|
703 | if timeout == -1: |
---|
704 | timeout = self.timeout |
---|
705 | |
---|
706 | try: |
---|
707 | #ED# incoming = '' |
---|
708 | incoming = self.buffer |
---|
709 | while 1: # Keep reading until exception or return. |
---|
710 | #ED# c = self.read_nonblocking (1, timeout) |
---|
711 | #ED# incoming = incoming + c |
---|
712 | |
---|
713 | # Sequence through the list of patterns and look for a match. |
---|
714 | index = -1 |
---|
715 | for cre in pattern_list: |
---|
716 | index = index + 1 |
---|
717 | if cre is EOF or cre is TIMEOUT: |
---|
718 | continue # The patterns for PexpectExceptions are handled differently. |
---|
719 | match = cre.search(incoming) |
---|
720 | if match is not None: |
---|
721 | self.before = incoming[ : match.start()] |
---|
722 | self.after = incoming[match.start() : ] |
---|
723 | self.match = match |
---|
724 | self.buffer = incoming[match.end() : ] |
---|
725 | return index |
---|
726 | # Read more data |
---|
727 | c = self.read_nonblocking (self.maxread, timeout) |
---|
728 | incoming = incoming + c |
---|
729 | |
---|
730 | except EOF: |
---|
731 | if EOF in pattern_list: |
---|
732 | self.before = incoming |
---|
733 | self.after = EOF |
---|
734 | self.buffer = '' |
---|
735 | return pattern_list.index(EOF) |
---|
736 | else: |
---|
737 | raise |
---|
738 | except TIMEOUT: |
---|
739 | if TIMEOUT in pattern_list: |
---|
740 | self.before = incoming |
---|
741 | self.after = TIMEOUT |
---|
742 | self.buffer = '' |
---|
743 | return pattern_list.index(TIMEOUT) |
---|
744 | else: |
---|
745 | raise |
---|
746 | except Exception: |
---|
747 | self.before = incoming |
---|
748 | self.after = None |
---|
749 | self.match = None |
---|
750 | self.buffer = '' |
---|
751 | raise |
---|
752 | |
---|
753 | def getwinsize(self): |
---|
754 | """ |
---|
755 | This returns the window size of the child tty. |
---|
756 | The return value is a tuple of (rows, cols). |
---|
757 | """ |
---|
758 | |
---|
759 | s = struct.pack('HHHH', 0, 0, 0, 0) |
---|
760 | x = fcntl.ioctl(self.fileno(), termios.TIOCGWINSZ, s) |
---|
761 | return struct.unpack('HHHH', x)[0:2] |
---|
762 | |
---|
763 | def setwinsize(self, r, c): |
---|
764 | """ |
---|
765 | This sets the windowsize of the child tty. |
---|
766 | This will cause a SIGWINCH signal to be sent to the child. |
---|
767 | This does not change the physical window size. |
---|
768 | It changes the size reported to TTY-aware applications like |
---|
769 | vi or curses -- applications that respond to the SIGWINCH signal. |
---|
770 | """ |
---|
771 | # Check for buggy platforms. Some Python versions on some platforms |
---|
772 | # (notably OSF1 Alpha and RedHat 7.1) truncate the value for |
---|
773 | # termios.TIOCSWINSZ. It is not clear why this happens. |
---|
774 | # These platforms don't seem to handle the signed int very well; |
---|
775 | # yet other platforms like OpenBSD have a large negative value for |
---|
776 | # TIOCSWINSZ and they don't have a truncate problem. |
---|
777 | # Newer versions of Linux have totally different values for TIOCSWINSZ. |
---|
778 | # |
---|
779 | # Note that this fix is a hack. |
---|
780 | TIOCSWINSZ = termios.TIOCSWINSZ |
---|
781 | if TIOCSWINSZ == 2148037735L: # L is not required in Python >= 2.2. |
---|
782 | TIOCSWINSZ = -2146929561 # Same bits, but with sign. |
---|
783 | |
---|
784 | # Note, assume ws_xpixel and ws_ypixel are zero. |
---|
785 | s = struct.pack('HHHH', r, c, 0, 0) |
---|
786 | fcntl.ioctl(self.fileno(), TIOCSWINSZ, s) |
---|
787 | |
---|
788 | def interact(self, escape_character = chr(29)): |
---|
789 | """This gives control of the child process to the interactive user |
---|
790 | (the human at the keyboard). |
---|
791 | Keystrokes are sent to the child process, and the stdout and stderr |
---|
792 | output of the child process is printed. |
---|
793 | When the user types the escape_character this method will stop. |
---|
794 | The default for escape_character is ^] (ASCII 29). |
---|
795 | This simply echos the child stdout and child stderr to the real |
---|
796 | stdout and it echos the real stdin to the child stdin. |
---|
797 | """ |
---|
798 | # Flush the buffer. |
---|
799 | self.stdout.write (self.buffer) |
---|
800 | self.buffer = '' |
---|
801 | self.stdout.flush() |
---|
802 | mode = tty.tcgetattr(self.STDIN_FILENO) |
---|
803 | tty.setraw(self.STDIN_FILENO) |
---|
804 | try: |
---|
805 | self.__interact_copy(escape_character) |
---|
806 | finally: |
---|
807 | tty.tcsetattr(self.STDIN_FILENO, tty.TCSAFLUSH, mode) |
---|
808 | |
---|
809 | def __interact_writen(self, fd, data): |
---|
810 | """This is used by the interact() method. |
---|
811 | """ |
---|
812 | while data != '' and self.isalive(): |
---|
813 | n = os.write(fd, data) |
---|
814 | data = data[n:] |
---|
815 | def __interact_read(self, fd): |
---|
816 | """This is used by the interact() method. |
---|
817 | """ |
---|
818 | return os.read(fd, 1000) |
---|
819 | def __interact_copy(self, escape_character = None): |
---|
820 | """This is used by the interact() method. |
---|
821 | """ |
---|
822 | while self.isalive(): |
---|
823 | r, w, e = select.select([self.child_fd, self.STDIN_FILENO], [], []) |
---|
824 | if self.child_fd in r: |
---|
825 | data = self.__interact_read(self.child_fd) |
---|
826 | os.write(self.STDOUT_FILENO, data) |
---|
827 | if self.STDIN_FILENO in r: |
---|
828 | data = self.__interact_read(self.STDIN_FILENO) |
---|
829 | self.__interact_writen(self.child_fd, data) |
---|
830 | if escape_character in data: |
---|
831 | break |
---|
832 | |
---|
833 | ############################################################################## |
---|
834 | # End of Spawn |
---|
835 | ############################################################################## |
---|
836 | |
---|
837 | def _which (filename): |
---|
838 | """This takes a given filename; tries to find it in the |
---|
839 | environment path; then checks if it is executable. |
---|
840 | """ |
---|
841 | |
---|
842 | # Special case where filename already contains a path. |
---|
843 | if os.path.dirname(filename) != '': |
---|
844 | if os.access (filename, os.X_OK): |
---|
845 | return filename |
---|
846 | |
---|
847 | if not os.environ.has_key('PATH') or os.environ['PATH'] == '': |
---|
848 | p = os.defpath |
---|
849 | else: |
---|
850 | p = os.environ['PATH'] |
---|
851 | |
---|
852 | # Oddly enough this was the one line that made Pexpect |
---|
853 | # incompatible with Python 1.5.2. |
---|
854 | #pathlist = p.split (os.pathsep) |
---|
855 | pathlist = string.split (p, os.pathsep) |
---|
856 | |
---|
857 | for path in pathlist: |
---|
858 | f = os.path.join(path, filename) |
---|
859 | if os.access(f, os.X_OK): |
---|
860 | return f |
---|
861 | return None |
---|
862 | |
---|
863 | def _split_command_line(command_line): |
---|
864 | """This splits a command line into a list of arguments. |
---|
865 | It splits arguments on spaces, but handles |
---|
866 | embedded quotes, doublequotes, and escaped characters. |
---|
867 | It's impossible to do this with a regular expression, so |
---|
868 | I wrote a little state machine to parse the command line. |
---|
869 | """ |
---|
870 | arg_list = [] |
---|
871 | arg = '' |
---|
872 | state_quote = 0 |
---|
873 | state_doublequote = 0 |
---|
874 | state_esc = 0 |
---|
875 | for c in command_line: |
---|
876 | if c == '\\': # Escape the next character |
---|
877 | state_esc = 1 |
---|
878 | elif c == r"'": # Handle single quote |
---|
879 | if state_esc: |
---|
880 | state_esc = 0 |
---|
881 | elif not state_quote: |
---|
882 | state_quote = 1 |
---|
883 | else: |
---|
884 | state_quote = 0 |
---|
885 | elif c == r'"': # Handle double quote |
---|
886 | if state_esc: |
---|
887 | state_esc = 0 |
---|
888 | elif not state_doublequote: |
---|
889 | state_doublequote = 1 |
---|
890 | else: |
---|
891 | state_doublequote = 0 |
---|
892 | |
---|
893 | # Add arg to arg_list unless in some other state. |
---|
894 | elif c == ' 'and not state_quote and not state_doublequote and not state_esc: |
---|
895 | arg_list.append(arg) |
---|
896 | arg = '' |
---|
897 | else: |
---|
898 | arg = arg + c |
---|
899 | if c != '\\'and state_esc: # escape mode lasts for one character. |
---|
900 | state_esc = 0 |
---|
901 | |
---|
902 | # Handle last argument. |
---|
903 | if arg != '': |
---|
904 | arg_list.append(arg) |
---|
905 | return arg_list |
---|
906 | |
---|
907 | import shlex |
---|
908 | def _split_command_line(command_line): |
---|
909 | return shlex.split(command_line) |
---|
910 | |
---|
911 | |
---|
912 | #################### |
---|
913 | # |
---|
914 | # NOTES |
---|
915 | # |
---|
916 | #################### |
---|
917 | |
---|
918 | ## def send_human(self, text, delay_min = 0, delay_max = 1): |
---|
919 | ## pass |
---|
920 | ## def spawn2(self, command, args): |
---|
921 | ## """return pid, fd_stdio, fd_stderr |
---|
922 | ## """ |
---|
923 | ## pass |
---|
924 | |
---|
925 | |
---|
926 | # Reason for double fork: |
---|
927 | # http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC15 |
---|
928 | # Reason for ptys: |
---|
929 | # http://www.erlenstar.demon.co.uk/unix/faq_4.html#SEC52 |
---|
930 | |
---|
931 | # Nonblocking on Win32? |
---|
932 | # Reasearch this as a way to maybe make pipe work for Win32. |
---|
933 | # http://groups.google.com/groups?q=setraw+tty&hl=en&selm=uvgpvisvk.fsf%40roundpoint.com&rnum=7 |
---|
934 | # |
---|
935 | # if istty: |
---|
936 | # if os.name=='posix': |
---|
937 | # import tty |
---|
938 | # tty.setraw(sys.stdin.fileno()) |
---|
939 | # elif os.name=='nt': |
---|
940 | # import win32file, win32con |
---|
941 | # hstdin = win32file._get_osfhandle(sys.stdin.fileno()) |
---|
942 | # modes = (win32file.GetConsoleMode(hstdin) |
---|
943 | # & ~(win32con.ENABLE_LINE_INPUT |
---|
944 | # |win32con.ENABLE_ECHO_INPUT)) |
---|
945 | # win32file.SetConsoleMode(hstdin, modes) |
---|
946 | |
---|
947 | # Basic documentation: |
---|
948 | # Explain use of lists of patterns and return index. |
---|
949 | # Explain exceptions for non-handled special cases like EOF |
---|
950 | |
---|
951 | # Test bad fork |
---|
952 | # Test ENOENT. In other words, no more TTY devices. |
---|
953 | |
---|
954 | #GLOBAL_SIGCHLD_RECEIVED = 0 |
---|
955 | #def childdied (signum, frame): |
---|
956 | # print 'Signal handler called with signal', signum |
---|
957 | # frame.f_globals['pexpect'].GLOBAL_SIGCHLD_RECEIVED = 1 |
---|
958 | # print str(frame.f_globals['pexpect'].GLOBAL_SIGCHLD_RECEIVED) |
---|
959 | # GLOBAL_SIGCHLD_RECEIVED = 1 |
---|
960 | |
---|
961 | ### Weird bug. If you read too fast after doing a sendline() |
---|
962 | # Sometimes you will read the data back that you just sent even if |
---|
963 | # the child did not echo the data. This is particularly a problem if |
---|
964 | # you send a password. |
---|
965 | |
---|
966 | # This was previously used to implement a look-ahead in reads. |
---|
967 | # if the lookahead failed then Pexpect would "push-back" the data |
---|
968 | # that was read. The idea was to allow read() to read blocks of data. |
---|
969 | # What I do now is just read one character at a time and then try a |
---|
970 | # match. This is not as efficient, but it works well enough for the |
---|
971 | # output of most applications and it makes program logic much simpler. |
---|
972 | ##class PushbackReader: |
---|
973 | ## """This class is a wrapper around os.read. It adds the features of buffering |
---|
974 | ## to allow push-back of data and to provide a timeout on a read. |
---|
975 | ## """ |
---|
976 | ## def __init__(self, file_descriptor): |
---|
977 | ## self.fd = file_descriptor |
---|
978 | ## self.buffer = '' |
---|
979 | ## |
---|
980 | ## def read(self, n, timeout = None): |
---|
981 | ## """This does a read restricted by a timeout and |
---|
982 | ## it includes any cached data from previous calls. |
---|
983 | ## This is a non-blocking wrapper around os.read. |
---|
984 | ## it uses select.select to supply a timeout. |
---|
985 | ## Note that if this is called with timeout=None (the default) |
---|
986 | ## then this actually MAY block. |
---|
987 | ## """ |
---|
988 | ## # The read() call is a problem. |
---|
989 | ## # Some platforms return an empty string '' at EOF. |
---|
990 | ## # Whereas other platforms raise an Input/output exception. |
---|
991 | ## |
---|
992 | ## avail = len(self.buffer) |
---|
993 | ## if n > avail: |
---|
994 | ## result = self.buffer |
---|
995 | ## n = n-avail |
---|
996 | ## else: |
---|
997 | ## result = self.buffer[: n] |
---|
998 | ## self.buffer = self.buffer[n:] |
---|
999 | ## |
---|
1000 | ## r, w, e = select.select([self.fd], [], [], timeout) |
---|
1001 | ## if not r: |
---|
1002 | ## self.flag_timeout = 1 |
---|
1003 | ## raise TIMEOUT('Read exceeded time: %d'%timeout) |
---|
1004 | ## |
---|
1005 | ## if self.fd in r: |
---|
1006 | ## try: |
---|
1007 | ## s = os.read(self.fd, n) |
---|
1008 | ## except OSError, e: |
---|
1009 | ## self.flag_eof = 1 |
---|
1010 | ## raise EOF('Read reached End Of File (EOF). Exception platform.') |
---|
1011 | ## if s == '': |
---|
1012 | ## self.flag_eof = 1 |
---|
1013 | ## raise EOF('Read reached End Of File (EOF). Empty string platform.') |
---|
1014 | ## return s |
---|
1015 | ## |
---|
1016 | ## self.flag_error = 1 |
---|
1017 | ## raise ExceptionPexpect('PushbackReader.read() reached an unexpected state.'+ |
---|
1018 | ## ' There is a logic error in the Pexpect source code.') |
---|
1019 | ## |
---|
1020 | ## def pushback(self, data): |
---|
1021 | ## self.buffer = piece+self.buffer |
---|
1022 | |
---|
1023 | |
---|
1024 | #def _setwinsize(r, c): |
---|
1025 | # """This sets the windowsize of the tty for stdout. |
---|
1026 | # This does not change the physical window size. |
---|
1027 | # It changes the size reported to TTY-aware applications like |
---|
1028 | # vi or curses -- applications that respond to the SIGWINCH signal. |
---|
1029 | # This is used by __spawn to set the tty window size of the child. |
---|
1030 | # """ |
---|
1031 | # # Check for buggy platforms. Some Pythons on some platforms |
---|
1032 | # # (notably OSF1 Alpha and RedHat 7.1) truncate the value for |
---|
1033 | # # termios.TIOCSWINSZ. It is not clear why this happens. |
---|
1034 | # # These platforms don't seem to handle the signed int very well; |
---|
1035 | # # yet other platforms like OpenBSD have a large negative value for |
---|
1036 | # # TIOCSWINSZ and they don't truncate. |
---|
1037 | # # Newer versions of Linux have totally different values for TIOCSWINSZ. |
---|
1038 | # # |
---|
1039 | # # Note that this fix is a hack. |
---|
1040 | # TIOCSWINSZ = termios.TIOCSWINSZ |
---|
1041 | # if TIOCSWINSZ == 2148037735L: # L is not required in Python 2.2. |
---|
1042 | # TIOCSWINSZ = -2146929561 # Same number in binary, but with sign. |
---|
1043 | # |
---|
1044 | # # Assume ws_xpixel and ws_ypixel are zero. |
---|
1045 | # s = struct.pack("HHHH", r, c, 0, 0) |
---|
1046 | # fcntl.ioctl(sys.stdout.fileno(), TIOCSWINSZ, s) |
---|
1047 | # |
---|
1048 | #def _getwinsize(): |
---|
1049 | # s = struct.pack("HHHH", 0, 0, 0, 0) |
---|
1050 | # x = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, s) |
---|
1051 | # return struct.unpack("HHHH", x) |
---|
1052 | |
---|