diff options
author | Matthew Newhook <matthew@zeroc.com> | 2007-08-24 14:41:39 +0800 |
---|---|---|
committer | Matthew Newhook <matthew@zeroc.com> | 2007-08-24 14:41:39 +0800 |
commit | 9ff48829c8669fbef8c7ca6a9b8bab99c77db2f9 (patch) | |
tree | 7fa0a0406be0386a8314b2e8c742f29bf127b70d /demoscript | |
parent | Merge branch 'bug1319' (diff) | |
download | ice-9ff48829c8669fbef8c7ca6a9b8bab99c77db2f9.tar.bz2 ice-9ff48829c8669fbef8c7ca6a9b8bab99c77db2f9.tar.xz ice-9ff48829c8669fbef8c7ca6a9b8bab99c77db2f9.zip |
Squashed commit of the following:
commit d74cec9e6c77e70b33883f517b1bb6f97b2ffa5e
Author: Matthew Newhook <matthew@zeroc.com>
Date: Fri Aug 24 14:33:06 2007 +0800
updates.
commit 643b31f979a00ea5397206ca59f20bb58c62660e
Author: Matthew Newhook <matthew@zeroc.com>
Date: Fri Aug 24 14:32:35 2007 +0800
added some .gitignore rules.
commit 1510d38798976753b435f32308e79acd9fcd2722
Author: Matthew Newhook <matthew@zeroc.com>
Date: Fri Aug 24 14:05:10 2007 +0800
removed bogus use of 127.0.0.1
commit 3c91e89b01af20a0dacfdbc1fbeca1c67c796656
Author: Matthew Newhook <matthew@zeroc.com>
Date: Fri Aug 24 14:01:43 2007 +0800
Verify that cygwin python is used under Windows.
commit ff160725c051eedb28a42d9dd69f0d8aa297e522
Author: Matthew Newhook <matthew@zeroc.com>
Date: Fri Aug 24 13:55:03 2007 +0800
bump timeout for the node to terminate. killing the node leaves the servers running under cygwin
commit ec3bddb58e861f906bf39aee1fa818c3e9a50000
Author: Matthew Newhook <matthew@zeroc.com>
Date: Fri Aug 24 13:48:55 2007 +0800
added back accidently removed files.
commit f9ea2f552d7f576ebfa878669925f4184a89257b
Author: Matthew Newhook <matthew@zeroc.com>
Date: Fri Aug 24 13:47:47 2007 +0800
http://bugzilla.zeroc.com/bugzilla/show_bug.cgi?id=2427, http://bugzilla.zeroc.com/bugzilla/show_bug.cgi?id=2422
commit 8a309ca0036dba7ecbd577edb2d737fa41174a5a
Author: Matthew Newhook <matthew@zeroc.com>
Date: Fri Aug 24 10:06:53 2007 +0800
added ruby support to the expect scripts.
commit 34597c1ee6d80d6754c9e925bd376125dd104a5f
Author: Matthew Newhook <matthew@zeroc.com>
Date: Fri Aug 24 09:26:10 2007 +0800
added pexpect to git repository.
commit 4bd2e1f2b52bcb9906b03ac216db686b8229b545
Author: Matthew Newhook <matthew@zeroc.com>
Date: Thu Aug 23 18:49:51 2007 +0800
numerous UNIX expect script fixes.
commit 25ab4a812f45a6e908d5d2d7ab03e557d79afa9f
Author: Matthew Newhook <matthew@zeroc.com>
Date: Thu Aug 23 16:47:08 2007 +0800
generate configuration files for IceGrid icebox/iceboxd
commit 6bb57bf7ed21d7256963c228aaf0b20657367a47
Author: Matthew Newhook <matthew@zeroc.com>
Date: Thu Aug 23 15:16:55 2007 +0800
added python & C# support to the expect scripts.
commit 08f1a59971ad3a52a4357069554dcc309b2d062b
Author: Matthew Newhook <matthew@zeroc.com>
Date: Wed Aug 22 14:23:53 2007 +0800
java expect scripts complete.
commit 3918f4ff3beea3005001bc55c5502c0825d8b7ea
Author: Matthew Newhook <matthew@zeroc.com>
Date: Wed Aug 22 13:30:31 2007 +0800
first set of expect changes.
Diffstat (limited to 'demoscript')
-rwxr-xr-x | demoscript/Freeze/bench.py | 5 | ||||
-rwxr-xr-x | demoscript/Freeze/library.py | 10 | ||||
-rwxr-xr-x | demoscript/Freeze/phonebook.py | 10 | ||||
-rwxr-xr-x | demoscript/Glacier2/callback.py | 17 | ||||
-rwxr-xr-x | demoscript/Ice/async.py | 9 | ||||
-rwxr-xr-x | demoscript/Ice/bidir.py | 9 | ||||
-rwxr-xr-x | demoscript/Ice/callback.py | 9 | ||||
-rw-r--r-- | demoscript/Ice/hello.py | 17 | ||||
-rwxr-xr-x | demoscript/Ice/invoke.py | 8 | ||||
-rwxr-xr-x | demoscript/Ice/nested.py | 9 | ||||
-rwxr-xr-x | demoscript/Ice/session.py | 14 | ||||
-rwxr-xr-x | demoscript/Ice/throughput.py | 53 | ||||
-rwxr-xr-x | demoscript/Ice/value.py | 8 | ||||
-rwxr-xr-x | demoscript/IceBox/hello.py | 22 | ||||
-rwxr-xr-x | demoscript/IceGrid/allocate.py | 24 | ||||
-rwxr-xr-x | demoscript/IceGrid/sessionActivation.py | 14 | ||||
-rwxr-xr-x | demoscript/IceGrid/simple.py | 42 | ||||
-rwxr-xr-x | demoscript/IceStorm/clock.py | 21 | ||||
-rw-r--r-- | demoscript/Util.py | 100 | ||||
-rwxr-xr-x | demoscript/book/freeze_filesystem.py | 8 | ||||
-rwxr-xr-x | demoscript/book/lifecycle.py | 8 | ||||
-rw-r--r-- | demoscript/pexpect.py | 1393 |
22 files changed, 1642 insertions, 168 deletions
diff --git a/demoscript/Freeze/bench.py b/demoscript/Freeze/bench.py index 67934186d87..1270396bc49 100755 --- a/demoscript/Freeze/bench.py +++ b/demoscript/Freeze/bench.py @@ -8,7 +8,7 @@ # # ********************************************************************** -import pexpect, sys +import sys def run(client, isJava=False): if isJava: @@ -63,6 +63,5 @@ def run(client, isJava=False): print "%s " % (client.before) print "IntIntMap with index (read test):" - client.expect(pexpect.EOF, timeout=200) - assert client.wait() == 0 + client.waitTestSuccess(timeout=200) print "%s " % (client.before) diff --git a/demoscript/Freeze/library.py b/demoscript/Freeze/library.py index 9212c18138f..9bed186678a 100755 --- a/demoscript/Freeze/library.py +++ b/demoscript/Freeze/library.py @@ -8,7 +8,8 @@ # # ********************************************************************** -import pexpect, sys +import sys +import demoscript.pexpect as pexpect def dequote(s): cur = 0 @@ -146,4 +147,11 @@ def run(client, server): client.sendline('remove') #server.expect(['locate could not find', 'locate found.*but it was dead or destroyed']) client.expect('current book no longer exists') + + client.sendline('shutdown') + if client != server: + server.waitTestSuccess() + client.sendline('exit') + client.waitTestSuccess() + print "ok" diff --git a/demoscript/Freeze/phonebook.py b/demoscript/Freeze/phonebook.py index a2d14bf2d14..aca68a95996 100755 --- a/demoscript/Freeze/phonebook.py +++ b/demoscript/Freeze/phonebook.py @@ -8,7 +8,8 @@ # # ********************************************************************** -import pexpect, sys +import sys +import demoscript.pexpect as pexpect def dequote(s): cur = 0 @@ -87,4 +88,11 @@ def run(client, server): client.expect('current contact no longer exists') client.sendline('find foo') client.expect('number of contacts found: 0') + + client.sendline('shutdown') + if client != server: + server.waitTestSuccess() + client.sendline('exit') + client.waitTestSuccess() + print "ok" diff --git a/demoscript/Glacier2/callback.py b/demoscript/Glacier2/callback.py index f9a387d6482..840dd0d2cec 100755 --- a/demoscript/Glacier2/callback.py +++ b/demoscript/Glacier2/callback.py @@ -8,7 +8,8 @@ # # ********************************************************************** -import pexpect, sys, time +import sys, time, signal +import demoscript.pexpect as pexpect def run(client, server, sessionserver, glacier2): print "testing ", @@ -77,3 +78,17 @@ def run(client, server, sessionserver, glacier2): glacier2.expect('expiring session') sessionserver.expect('destroying session for user') print "ok" + + # SessionNotExist + client.sendline('x') + client.expect('SessionNotExistException') + client.waitTestSuccess() + + sessionserver.kill(signal.SIGINT) + sessionserver.waitTestSuccess() + + server.kill(signal.SIGINT) + server.waitTestSuccess() + + glacier2.kill(signal.SIGINT) + glacier2.waitTestSuccess() diff --git a/demoscript/Ice/async.py b/demoscript/Ice/async.py index aa1449db294..122d425d531 100755 --- a/demoscript/Ice/async.py +++ b/demoscript/Ice/async.py @@ -8,7 +8,8 @@ # # ********************************************************************** -import pexpect, sys +import sys +import demoscript.pexpect as pexpect def run(client, server): print "testing client... ", @@ -29,11 +30,9 @@ def run(client, server): sys.stdout.flush() client.sendline('d') client.sendline('s') - server.expect(pexpect.EOF) - assert server.wait() == 0 + server.waitTestSuccess() client.expect('RequestCanceledException') client.sendline('x') - client.expect(pexpect.EOF) - assert client.wait() == 0 + client.waitTestSuccess() print "ok" diff --git a/demoscript/Ice/bidir.py b/demoscript/Ice/bidir.py index 1ec7a13c5ce..711707bc4eb 100755 --- a/demoscript/Ice/bidir.py +++ b/demoscript/Ice/bidir.py @@ -8,7 +8,7 @@ # # ********************************************************************** -import pexpect, sys, signal, demoscript +import sys, signal, demoscript def run(clientStr, server): print "adding client 1... ", @@ -28,10 +28,17 @@ def run(clientStr, server): print "removing client 2...", client2.kill(signal.SIGINT) + client2.waitTestSuccess(timeout=20) server.expect('removing client') client1.expect('received callback #') print "ok" + print "removing client 1...", client1.kill(signal.SIGINT) + client1.waitTestSuccess() server.expect('removing client') + + server.kill(signal.SIGINT) + server.waitTestSuccess() + print "ok" diff --git a/demoscript/Ice/callback.py b/demoscript/Ice/callback.py index c90d048c06d..2e7394c4a49 100755 --- a/demoscript/Ice/callback.py +++ b/demoscript/Ice/callback.py @@ -8,7 +8,8 @@ # # ********************************************************************** -import pexpect, sys +import sys +import demoscript.pexpect as pexpect def runtests(client, server, secure): print "testing twoway", @@ -66,9 +67,7 @@ def run(client, server): runtests(client, server, True) client.sendline('s') - server.expect(pexpect.EOF) - assert server.wait() == 0 + server.waitTestSuccess() client.sendline('x') - client.expect(pexpect.EOF) - assert client.wait() == 0 + client.waitTestSuccess() diff --git a/demoscript/Ice/hello.py b/demoscript/Ice/hello.py index 243eb0abf65..9ae18f2c967 100644 --- a/demoscript/Ice/hello.py +++ b/demoscript/Ice/hello.py @@ -8,7 +8,8 @@ # # ********************************************************************** -import pexpect, sys +import sys +import demoscript.pexpect as pexpect def runtests(client, server, secure): print "testing twoway", @@ -56,7 +57,13 @@ def runtests(client, server, secure): client.sendline('T') client.sendline('P') client.sendline('t') - client.expect('.*TimeoutException.*') + # With Java/C# under Windows the tcp connection shutdown takes + # longer than expected... hence we use a 6 second timeout instead + # of the expected ~4s. + # + # http://bugzilla/bugzilla/show_bug.cgi?id=2425 + # + client.expect('.*TimeoutException.*', timeout=6) server.expect('Hello World!') server.expect('Hello World!') # second because op is idempotent client.sendline('P') @@ -75,9 +82,7 @@ def run(client, server): runtests(client, server, True) client.sendline('s') - server.expect(pexpect.EOF) - assert server.wait() == 0 + server.waitTestSuccess() client.sendline('x') - client.expect(pexpect.EOF) - assert client.wait() == 0 + client.waitTestSuccess() diff --git a/demoscript/Ice/invoke.py b/demoscript/Ice/invoke.py index 60c5392633d..8aedd2a7e17 100755 --- a/demoscript/Ice/invoke.py +++ b/demoscript/Ice/invoke.py @@ -8,7 +8,7 @@ # # ********************************************************************** -import pexpect, sys +import sys def run(client, server): print "testing...", @@ -36,9 +36,7 @@ def run(client, server): print "ok" client.sendline('s') - server.expect(pexpect.EOF) - assert server.wait() == 0 + server.waitTestSuccess() client.sendline('x') - client.expect(pexpect.EOF) - assert client.wait() == 0 + client.waitTestSuccess() diff --git a/demoscript/Ice/nested.py b/demoscript/Ice/nested.py index 155d9818b03..934f3925573 100755 --- a/demoscript/Ice/nested.py +++ b/demoscript/Ice/nested.py @@ -8,7 +8,7 @@ # # ********************************************************************** -import pexpect, sys, signal +import sys, signal def run(client, server): print "testing nested...", @@ -34,10 +34,7 @@ def run(client, server): print "ok" client.sendline('x') - client.expect(pexpect.EOF) - assert client.wait() == 0 + client.waitTestSuccess() server.kill(signal.SIGINT) - server.expect(pexpect.EOF) - status = server.wait() - assert status == 0 or status == 130 or server.signalstatus == signal.SIGINT + server.waitTestSuccess() diff --git a/demoscript/Ice/session.py b/demoscript/Ice/session.py index 1a5691552c2..350d4d23cb5 100755 --- a/demoscript/Ice/session.py +++ b/demoscript/Ice/session.py @@ -8,7 +8,7 @@ # # ********************************************************************** -import pexpect, sys, demoscript +import sys, demoscript def run(clientStr, server): client = demoscript.Util.spawn(clientStr) @@ -24,8 +24,7 @@ def run(clientStr, server): client.sendline('1') client.expect('Index is too high') client.sendline('x') - client.expect(pexpect.EOF) - assert client.wait() == 0 + client.waitTestSuccess() server.expect("The session foo is now destroyed.") print "ok" @@ -37,8 +36,7 @@ def run(clientStr, server): server.expect('The session foo is now created.') client.sendline('c') client.sendline('t') - client.expect(pexpect.EOF) - assert client.wait() == 0 + client.waitTestSuccess() server.expect("The session foo is now destroyed.\r{1,2}\n.*The session foo has timed out.", timeout=25) print "ok" @@ -47,9 +45,7 @@ def run(clientStr, server): client.sendline('foo') server.expect('The session foo is now created.') client.sendline('s') - server.expect(pexpect.EOF) - assert server.wait() == 0 + server.waitTestSuccess() client.sendline('x') - client.expect(pexpect.EOF) - assert client.wait() == 0 + client.waitTestSuccess() diff --git a/demoscript/Ice/throughput.py b/demoscript/Ice/throughput.py index a9c3c968c08..f490fa0788c 100755 --- a/demoscript/Ice/throughput.py +++ b/demoscript/Ice/throughput.py @@ -8,7 +8,7 @@ # # ********************************************************************** -import pexpect, sys, demoscript +import sys, demoscript def runseries(client): client.expect('==> ', timeout=240) @@ -17,45 +17,42 @@ def runseries(client): client.expect('==> ', timeout=240) print "twoway: %s " % (client.before) + if not demoscript.Util.fast: + client.sendline('o') - client.sendline('o') + client.expect('o') + client.expect('==> ', timeout=240) + print "oneway: %s " % (client.before) - client.expect('o') - client.expect('==> ', timeout=240) - print "oneway: %s " % (client.before) + client.sendline('r') + client.expect('r') - client.sendline('r') - client.expect('r') + client.expect('==> ', timeout=240) + print "receive: %s" % (client.before) + client.sendline('e') + client.expect('e') - client.expect('==> ', timeout=240) - print "receive: %s" % (client.before) - client.sendline('e') - client.expect('e') - - client.expect('==> ', timeout=240) - print "echo: %s" % (client.before) + client.expect('==> ', timeout=240) + print "echo: %s" % (client.before) def run(client, server): print "testing bytes" runseries(client) - if not demoscript.Util.fast: - print "testing strings" - client.sendline('2') - runseries(client) + print "testing strings" + client.sendline('2') + runseries(client) - print "testing structs with string... " - client.sendline('3') - runseries(client) + print "testing structs with string... " + client.sendline('3') + runseries(client) - print "testing structs with two ints and double... " - client.sendline('4') - runseries(client) + print "testing structs with two ints and double... " + client.sendline('4') + runseries(client) client.sendline('s') - server.expect(pexpect.EOF) - assert server.wait() == 0 + server.waitTestSuccess() client.sendline('x') - client.expect(pexpect.EOF) - assert client.wait() == 0 + client.waitTestSuccess() diff --git a/demoscript/Ice/value.py b/demoscript/Ice/value.py index 9abacc8b3e1..9d29368179a 100755 --- a/demoscript/Ice/value.py +++ b/demoscript/Ice/value.py @@ -8,7 +8,7 @@ # # ********************************************************************** -import pexpect, sys +import sys def run(client, server, ruby = False): print "testing...", @@ -42,7 +42,5 @@ def run(client, server, ruby = False): client.expect('==> a derived message 4 u\r{1,2}\n==> A DERIVED MESSAGE 4 U') print "ok" - server.expect(pexpect.EOF) - assert server.wait() == 0 - client.expect(pexpect.EOF) - assert client.wait() == 0 + server.waitTestSuccess() + client.waitTestSuccess() diff --git a/demoscript/IceBox/hello.py b/demoscript/IceBox/hello.py index b58d75ecbf1..7932dd64ad2 100755 --- a/demoscript/IceBox/hello.py +++ b/demoscript/IceBox/hello.py @@ -8,7 +8,8 @@ # # ********************************************************************** -import pexpect, sys, demoscript +import sys, demoscript +import demoscript.pexpect as pexpect def runtests(client, server, secure): print "testing twoway", @@ -54,20 +55,17 @@ def runtests(client, server, secure): def run(client, server): runtests(client, server, False) - print "repeating tests with SSL" + if not demoscript.Util.isMono(): + print "repeating tests with SSL" - client.sendline('S') + client.sendline('S') - runtests(client, server, True) + runtests(client, server, True) client.sendline('x') - client.expect(pexpect.EOF) - assert client.wait() == 0 + client.waitTestSuccess() - admin = demoscript.Util.spawn('iceboxadmin --IceBox.InstanceName=DemoIceBox --IceBox.ServiceManager.Endpoints="tcp -p 9998:ssl -p 9999" shutdown') + admin = demoscript.Util.spawn('iceboxadmin --IceBox.InstanceName=DemoIceBox --IceBox.ServiceManager.Endpoints="tcp -p 9998:ssl -p 9999" shutdown', language="C++") - admin.expect(pexpect.EOF) - assert admin.wait() == 0 - - server.expect(pexpect.EOF) - assert server.wait() == 0 + admin.waitTestSuccess() + server.waitTestSuccess() diff --git a/demoscript/IceGrid/allocate.py b/demoscript/IceGrid/allocate.py index 25e08e20819..134aee21181 100755 --- a/demoscript/IceGrid/allocate.py +++ b/demoscript/IceGrid/allocate.py @@ -8,7 +8,8 @@ # # ********************************************************************** -import pexpect, sys, demoscript +import sys, demoscript +import demoscript.pexpect as pexpect def run(clientCmd): print "cleaning databases...", @@ -58,8 +59,7 @@ def run(clientCmd): node.expect('says Hello World!') client2.expect(pexpect.TIMEOUT, timeout = 0) client1.sendline('x') - client1.expect(pexpect.EOF, timeout=1) - assert client1.wait() == 0 + client1.waitTestSuccess(timeout=1) client2.expect('==>') client2.sendline('t') @@ -67,8 +67,7 @@ def run(clientCmd): client2.sendline('s') node.expect('detected termination of server') client2.sendline('x') - client2.expect(pexpect.EOF, timeout=1) - assert client2.wait() == 0 + client2.waitTestSuccess(timeout=1) print "ok" print "deploying multiple...", @@ -109,8 +108,7 @@ def run(clientCmd): client3.expect(pexpect.TIMEOUT, timeout = 0) client1.sendline('x') - client1.expect(pexpect.EOF, timeout=1) - assert client1.wait() == 0 + client1.waitTestSuccess(timeout=1) client3.expect('==>') client3.sendline('t') @@ -121,20 +119,16 @@ def run(clientCmd): client2.sendline('s') node.expect('detected termination of server') client2.sendline('x') - client2.expect(pexpect.EOF, timeout=1) - assert client2.wait() == 0 + client2.waitTestSuccess(timeout=1) client3.sendline('s') node.expect('detected termination of server') client3.sendline('x') - client3.expect(pexpect.EOF, timeout=1) - assert client3.wait() == 0 + client3.waitTestSuccess(timeout=1) print "ok" admin.sendline('registry shutdown Master') admin.sendline('exit') - admin.expect(pexpect.EOF) - assert admin.wait() == 0 - node.expect(pexpect.EOF) - assert node.wait() == 0 + admin.waitTestSuccess() + node.waitTestSuccess() diff --git a/demoscript/IceGrid/sessionActivation.py b/demoscript/IceGrid/sessionActivation.py index d0d2609162b..d634438470c 100755 --- a/demoscript/IceGrid/sessionActivation.py +++ b/demoscript/IceGrid/sessionActivation.py @@ -8,7 +8,7 @@ # # ********************************************************************** -import pexpect, sys, demoscript +import sys, demoscript def run(clientCmd): print "cleaning databases...", @@ -49,8 +49,7 @@ def run(clientCmd): client.sendline('t') node.expect('says Hello World!') client.sendline('x') - client.expect(pexpect.EOF, timeout=1) - assert client.wait() == 0 + client.waitTestSuccess(timeout=1) node.expect('detected termination of server') client = demoscript.Util.spawn(clientCmd) @@ -63,15 +62,12 @@ def run(clientCmd): client.sendline('t') node.expect('says Hello World!') client.sendline('x') - client.expect(pexpect.EOF, timeout=1) - assert client.wait() == 0 + client.waitTestSuccess(timeout=1) node.expect('detected termination of server') print "ok" admin.sendline('registry shutdown Master') admin.sendline('exit') - admin.expect(pexpect.EOF) - assert admin.wait() == 0 - node.expect(pexpect.EOF) - assert node.wait() == 0 + admin.waitTestSuccess() + node.waitTestSuccess() diff --git a/demoscript/IceGrid/simple.py b/demoscript/IceGrid/simple.py index c40b38385b7..400c1461988 100755 --- a/demoscript/IceGrid/simple.py +++ b/demoscript/IceGrid/simple.py @@ -8,9 +8,9 @@ # # ********************************************************************** -import pexpect, sys, demoscript +import sys, demoscript -def run(clientStr): +def run(clientStr, desc = 'application'): print "cleaning databases...", sys.stdout.flush() demoscript.Util.cleanDbDir("db/node") @@ -24,15 +24,16 @@ def run(clientStr): print "starting icegridnode...", sys.stdout.flush() - node = demoscript.Util.spawn('icegridnode --Ice.Config=config.grid --Ice.PrintAdapterReady %s' % (args)) + node = demoscript.Util.spawn('icegridnode --Ice.Config=config.grid --Ice.PrintAdapterReady %s' % (args), + language="C++") node.expect('IceGrid.Registry.Internal ready\r{1,2}\nIceGrid.Registry.Server ready\r{1,2}\nIceGrid.Registry.Client ready\r{1,2}\nIceGrid.Node ready') print "ok" print "deploying application...", sys.stdout.flush() - admin = demoscript.Util.spawn('icegridadmin --Ice.Config=config.grid') + admin = demoscript.Util.spawn('icegridadmin --Ice.Config=config.grid', language="C++") admin.expect('>>>') - admin.sendline("application add \'application.xml\'") + admin.sendline("application add \'%s.xml\'" %(desc)) admin.expect('>>>') print "ok" @@ -48,13 +49,12 @@ def run(clientStr): node.expect("detected termination of.*SimpleServer") client.sendline('x') - client.expect(pexpect.EOF, timeout=1) - assert client.wait() == 0 + client.waitTestSuccess(timeout=1) print "ok" print "deploying template...", sys.stdout.flush() - admin.sendline("application update \'application_with_template.xml\'") + admin.sendline("application update \'%s_with_template.xml\'" % (desc)) admin.expect('>>>') print "ok" @@ -70,20 +70,19 @@ def run(clientStr): node.expect("detected termination of.*SimpleServer-[123]") client.sendline('x') - client.expect(pexpect.EOF, timeout=1) - assert client.wait() == 0 + client.waitTestSuccess(timeout=1) print "ok" print "deploying replicated version...", sys.stdout.flush() - admin.sendline("application update \'application_with_replication.xml\'") + admin.sendline("application update \'%s_with_replication.xml\'" %(desc)) admin.expect('>>> ') print "ok" print "testing client...", sys.stdout.flush() - client = demoscript.Util.spawn(clientStr + ' --Ice.Default.Host=127.0.0.1') + client = demoscript.Util.spawn(clientStr) client.expect('==>') client.sendline('t') node.expect("SimpleServer-1 says Hello World!") @@ -92,10 +91,9 @@ def run(clientStr): client.sendline('s') node.expect("detected termination of.*SimpleServer-1") client.sendline('x') - client.expect(pexpect.EOF, timeout=1) - assert client.wait() == 0 + client.waitTestSuccess(timeout=1) - client = demoscript.Util.spawn(clientStr + ' --Ice.Default.Host=127.0.0.1') + client = demoscript.Util.spawn(clientStr) client.expect('==>') client.sendline('t') node.expect("SimpleServer-2 says Hello World!") @@ -104,10 +102,9 @@ def run(clientStr): client.sendline('s') node.expect("detected termination of.*SimpleServer-2") client.sendline('x') - client.expect(pexpect.EOF, timeout=1) - assert client.wait() == 0 + client.waitTestSuccess(timeout=1) - client = demoscript.Util.spawn(clientStr + ' --Ice.Default.Host=127.0.0.1') + client = demoscript.Util.spawn(clientStr) client.expect('==>') client.sendline('t') node.expect("SimpleServer-3 says Hello World!") @@ -116,14 +113,11 @@ def run(clientStr): client.sendline('s') node.expect("detected termination of.*SimpleServer-3") client.sendline('x') - client.expect(pexpect.EOF, timeout=1) - assert client.wait() == 0 + client.waitTestSuccess(timeout=1) print "ok" admin.sendline('registry shutdown Master') admin.sendline('exit') - admin.expect(pexpect.EOF) - assert admin.wait() == 0 - node.expect(pexpect.EOF) - assert node.wait() == 0 + admin.waitTestSuccess() + node.waitTestSuccess() diff --git a/demoscript/IceStorm/clock.py b/demoscript/IceStorm/clock.py index e941fad7647..72ad9d83516 100755 --- a/demoscript/IceStorm/clock.py +++ b/demoscript/IceStorm/clock.py @@ -8,7 +8,8 @@ # # ********************************************************************** -import pexpect, sys, time, signal, demoscript +import sys, time, signal, demoscript +import demoscript.pexpect as pexpect def runtest(icestorm, subCmd, subargs, pubCmd, pubargs): print "testing pub%s/sub%s..." % (pubargs, subargs), @@ -25,14 +26,10 @@ def runtest(icestorm, subCmd, subargs, pubCmd, pubargs): sub.expect('[0-9][0-9]/[0-9][0-9].*\r{1,2}\n[0-9][0-9]/[0-9][0-9]') pub.kill(signal.SIGINT) - pub.expect(pexpect.EOF) - status = pub.wait() - assert status == 0 or status == 130 or pub.signalstatus == signal.SIGINT + pub.waitTestSuccess() sub.kill(signal.SIGINT) - sub.expect(pexpect.EOF) - status = sub.wait() - assert status == 0 or status == 130 or sub.signalstatus == signal.SIGINT + sub.waitTestSuccess() try: icestorm.expect('Unsubscribe:') except pexpect.TIMEOUT: @@ -50,7 +47,7 @@ def run(subCmd, pubCmd): else: args = '' - icestorm = demoscript.Util.spawn('%s --Ice.Config=config.icebox --Ice.PrintAdapterReady %s' % (demoscript.Util.getIceBox(), args)) + icestorm = demoscript.Util.spawn('%s --Ice.Config=config.icebox --Ice.PrintAdapterReady %s' % (demoscript.Util.getIceBox(), args), language="C++") icestorm.expect('.* ready') runtest(icestorm, subCmd, "", pubCmd, "") @@ -62,8 +59,6 @@ def run(subCmd, pubCmd): for s in pubargs: runtest(icestorm, subCmd, "", pubCmd, s) - admin = demoscript.Util.spawn('iceboxadmin --Ice.Config=config.icebox shutdown') - admin.expect(pexpect.EOF) - assert admin.wait() == 0 - icestorm.expect(pexpect.EOF) - assert icestorm.wait() == 0 + admin = demoscript.Util.spawn('iceboxadmin --Ice.Config=config.icebox shutdown', language="C++") + admin.waitTestSuccess() + icestorm.waitTestSuccess() diff --git a/demoscript/Util.py b/demoscript/Util.py index 6ad51faa61c..dcce6928348 100644 --- a/demoscript/Util.py +++ b/demoscript/Util.py @@ -7,6 +7,11 @@ # # ********************************************************************** +import sys +if sys.platform == "win32": + print "demoscript only supports cygwin python under Windows (use /usr/bin/python expect.py)" + sys.exit(1) + # # Timeout after the initial spawn. # @@ -26,7 +31,13 @@ host = "127.0.0.1" # debug = False -import sys, getopt, pexpect, os +# +# The test language. +# +defaultLanguage = None + +import getopt, os, signal +import demoscript.pexpect as pexpect def usage(): print "usage: " + sys.argv[0] + " --fast --trace --debug --host host --mode=[debug|release]" @@ -50,9 +61,8 @@ for o, a in opts: fast = True if o == "--mode": mode = a - if mode != 'debug' or mode != 'release': - print "usage: " + sys.argv[0] + " --trace --debug --host host --mode=[debug|release]" - sys.exit(2) + if mode != 'debug' and mode != 'release': + usage() if host != "": defaultHost = " --Ice.Default.Host=%s" % (host) @@ -64,20 +74,20 @@ def isCygwin(): # versions return variations like "cygwin_nt-4.01". return sys.platform[:6] == "cygwin" -def isWin32(): - return sys.platform == "win32" or isCygwin() - def isDarwin(): return sys.platform == "darwin" -def mono(): - if isWin32(): - return "" +def isMono(): + return not isCygwin() + +def python(): + if isCygwin(): + return "python -u " else: - return "mono " + return "python " def getIceBox(): - if isWin32(): + if isCygwin(): if mode == 'release': return "icebox" else: @@ -87,28 +97,92 @@ def getIceBox(): # Automatically adds default host, and uses our default timeout for # expect. class spawn(pexpect.spawn): - def __init__(self, command): + def __init__(self, command, language = None): if defaultHost: command = '%s %s' % (command, defaultHost) if debug: print '(%s)' % (command) + if not language: + self.language = defaultLanguage + else: + self.language = language self.expectFirst = True if trace: logfile = sys.stdout else: logfile = None + self.sentKill = None + if self.language == "C#": + if isMono(): + command = "mono " + command + else: + command = "./" + command + if self.language == "Python": + command = python() + command pexpect.spawn.__init__(self, command, logfile = logfile) + def expect(self, pattern, timeout = defaultTimeout, searchwindowsize=None): if self.expectFirst and timeout == defaultTimeout: timeout = initialTimeout self.expectFirst = False return pexpect.spawn.expect(self, pattern, timeout, searchwindowsize) + def wait(self): try: return pexpect.spawn.wait(self) except pexpect.ExceptionPexpect, e: return self.exitstatus + def kill(self, sig): + if isCygwin(): + sig = signal.SIGTERM + self.sentKill = sig + return pexpect.spawn.kill(self, sig) + + # status == 0 is normal exit status for C++ + # + # status == 130 is normal exit status for a Java app that was + # SIGINT interrupted. + # + # signalstatus == SIGINT is normal exit status for a mono app, + # or if under cygwin (since cygwin spawned expect apps cannot + # catch SIGINT). + # + def waitTestSuccess(self, exitstatus = 0, timeout = None): + if not timeout: + self.expect(pexpect.EOF) + else: + self.expect(pexpect.EOF, timeout) + status = self.wait() + if self.language == "C++" or self.language == "Python" or self.language == "Ruby" or self.language == "PHP": + if isCygwin() and self.sentKill: + assert self.signalstatus == self.sentKill + else: + assert status == exitstatus + elif self.language == "C#": + if isMono() or isCygwin() and self.sentKill: + assert self.signalstatus == self.sentKill + else: + assert status == exitstatus + elif self.language == "Java": + if self.sentKill: + if isCygwin(): + assert self.signalstatus == self.sentKill + else: + if self.sentKill == signal.SIGINT: + assert status == 130 + else: + assert False + else: + assert status == exitstatus + else: + # Unknown language + print "Warning: unknown language" + if not self.sentKill: + assert status == exitstatus + else: + assert status == exitstatus or status == 130 or self.signalstatus == self.sentKill + def cleanDbDir(path): for filename in [ os.path.join(path, f) for f in os.listdir(path) if f != ".gitignore" and f != "DB_CONFIG"]: if os.path.isdir(filename): diff --git a/demoscript/book/freeze_filesystem.py b/demoscript/book/freeze_filesystem.py index 131e5b557aa..58629aa4f67 100755 --- a/demoscript/book/freeze_filesystem.py +++ b/demoscript/book/freeze_filesystem.py @@ -8,7 +8,7 @@ # # ********************************************************************** -import pexpect, sys +import sys, signal def run(client, server): print "testing...", @@ -26,6 +26,8 @@ def run(client, server): j = client.expect(['Destroying Coleridge', 'Destroying README']) assert i != j server.expect('removed object') - client.expect(pexpect.EOF) - assert client.wait() == 0 + client.waitTestSuccess() print "ok" + + server.kill(signal.SIGINT) + server.waitTestSuccess() diff --git a/demoscript/book/lifecycle.py b/demoscript/book/lifecycle.py index 97baf28396f..39f14cc9c88 100755 --- a/demoscript/book/lifecycle.py +++ b/demoscript/book/lifecycle.py @@ -8,7 +8,7 @@ # # ********************************************************************** -import pexpect, sys +import sys, signal def run(client, server): print "testing...", @@ -162,6 +162,8 @@ def run(client, server): client.expect('ls\r{1,2}\n>') client.sendline('exit') - client.expect(pexpect.EOF) - assert client.wait() == 0 + client.waitTestSuccess() + + server.kill(signal.SIGINT) + server.waitTestSuccess() print "ok" diff --git a/demoscript/pexpect.py b/demoscript/pexpect.py new file mode 100644 index 00000000000..1326de1e869 --- /dev/null +++ b/demoscript/pexpect.py @@ -0,0 +1,1393 @@ +"""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 +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 +require TCL and Expect or require C extensions to be compiled. Pexpect does not +use C, Expect, or TCL extensions. It should work on any platform that supports +the standard Python pty module. The Pexpect interface focuses on ease of use so +that simple tasks are easy. + +There are two main interfaces to Pexpect -- the function, run() and the class, +spawn. You can call the run() function to execute a command and return the +output. This is a handy replacement for os.system(). + +For example: + pexpect.run('ls -la') + +The more powerful interface is the spawn class. You can use this to spawn an +external child command and then interact with the child by sending lines and +expecting responses. + +For example: + child = pexpect.spawn('scp foo myname@host.example.com:.') + child.expect ('Password:') + child.sendline (mypassword) + +This works even for commands that ask for passwords or other input outside of +the normal stdio streams. + +Credits: +Noah Spurrier, Richard Holden, Marco Molteni, Kimberley Burchett, Robert Stone, +Hartmut Goebel, Chad Schroeder, Erick Tryzelaar, Dave Kirby, Ids vander Molen, +George Todd, Noel Taylor, Nicolas D. Cesar, Alexander Gattin, +Geoffrey Marshall, Francisco Lourenco, Glen Mabey, Karthik Gurusamy, +Fernando Perez +(Let me know if I forgot anyone.) + +Free, open source, and all that good stuff. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Pexpect Copyright (c) 2006 Noah Spurrier +http://pexpect.sourceforge.net/ + +$Revision: 395 $ +$Date: 2006-05-31 20:07:18 -0700 (Wed, 31 May 2006) $ +""" +try: + import os, sys, time + import select + import string + import re + import struct + import resource + import types + import pty + import tty + import termios + import fcntl + import errno + import traceback + import signal +except ImportError, e: + raise ImportError (str(e) + """ +A critical module was not found. Probably this operating system does not support it. +Pexpect is intended for UNIX-like operating systems.""") + +__version__ = '2.1' +__revision__ = '$Revision: 395 $' +__all__ = ['ExceptionPexpect', 'EOF', 'TIMEOUT', 'spawn', 'run', 'which', 'split_command_line', + '__version__', '__revision__'] + +# Exception classes used by this module. +class ExceptionPexpect(Exception): + """Base class for all exceptions raised by this module. + """ + def __init__(self, value): + self.value = value + def __str__(self): + return str(self.value) + def get_trace(self): + """This returns an abbreviated stack trace with lines that only concern the caller. + In other words, the stack trace inside the Pexpect module is not included. + """ + tblist = traceback.extract_tb(sys.exc_info()[2]) + tblist = filter(self.__filter_not_pexpect, tblist) + tblist = traceback.format_list(tblist) + return ''.join(tblist) + def __filter_not_pexpect(self, trace_list_item): + if trace_list_item[0].find('pexpect.py') == -1: + return True + else: + return False +class EOF(ExceptionPexpect): + """Raised when EOF is read from a child. + """ +class TIMEOUT(ExceptionPexpect): + """Raised when a read time exceeds the timeout. + """ +##class TIMEOUT_PATTERN(TIMEOUT): +## """Raised when the pattern match time exceeds the timeout. +## This is different than a read TIMEOUT because the child process may +## give output, thus never give a TIMEOUT, but the output +## may never match a pattern. +## """ +##class MAXBUFFER(ExceptionPexpect): +## """Raised when a scan buffer fills before matching an expected pattern.""" + +def run (command, timeout=-1, withexitstatus=False, events=None, extra_args=None, logfile=None): + """This function runs the given command; waits for it to finish; + then returns all output as a string. STDERR is included in output. + If the full path to the command is not given then the path is searched. + + Note that lines are terminated by CR/LF (\\r\\n) combination + even on UNIX-like systems because this is the standard for pseudo ttys. + If you set withexitstatus to true, then run will return a tuple of + (command_output, exitstatus). If withexitstatus is false then this + returns just command_output. + + The run() function can often be used instead of creating a spawn instance. + For example, the following code uses spawn: + from pexpect import * + child = spawn('scp foo myname@host.example.com:.') + child.expect ('(?i)password') + child.sendline (mypassword) + The previous code can be replace with the following, which you may + or may not find simpler: + from pexpect import * + run ('scp foo myname@host.example.com:.', events={'(?i)password': mypassword}) + + Examples: + Start the apache daemon on the local machine: + from pexpect import * + run ("/usr/local/apache/bin/apachectl start") + Check in a file using SVN: + from pexpect import * + run ("svn ci -m 'automatic commit' my_file.py") + Run a command and capture exit status: + from pexpect import * + (command_output, exitstatus) = run ('ls -l /bin', withexitstatus=1) + + Tricky Examples: + The following will run SSH and execute 'ls -l' on the remote machine. + The password 'secret' will be sent if the '(?i)password' pattern is ever seen. + run ("ssh username@machine.example.com 'ls -l'", events={'(?i)password':'secret\n'}) + + This will start mencoder to rip a video from DVD. This will also display + progress ticks every 5 seconds as it runs. + from pexpect import * + def print_ticks(d): + print d['event_count'], + run ("mencoder dvd://1 -o video.avi -oac copy -ovc copy", events={TIMEOUT:print_ticks}, timeout=5) + + The 'events' argument should be a dictionary of patterns and responses. + Whenever one of the patterns is seen in the command out + run() will send the associated response string. Note that you should + put newlines in your string if Enter is necessary. + The responses may also contain callback functions. + Any callback is function that takes a dictionary as an argument. + The dictionary contains all the locals from the run() function, so + you can access the child spawn object or any other variable defined + in run() (event_count, child, and extra_args are the most useful). + A callback may return True to stop the current run process otherwise + run() continues until the next event. + A callback may also return a string which will be sent to the child. + 'extra_args' is not used by directly run(). It provides a way to pass data to + a callback function through run() through the locals dictionary passed to a callback. + """ + if timeout == -1: + child = spawn(command, maxread=2000, logfile=logfile) + else: + child = spawn(command, timeout=timeout, maxread=2000, logfile=logfile) + if events is not None: + patterns = events.keys() + responses = events.values() + else: + patterns=None # We assume that EOF or TIMEOUT will save us. + responses=None + child_result_list = [] + event_count = 0 + while 1: + try: + index = child.expect (patterns) + if type(child.after) is types.StringType: + child_result_list.append(child.before + child.after) + else: # child.after may have been a TIMEOUT or EOF, so don't cat those. + child_result_list.append(child.before) + if type(responses[index]) is types.StringType: + child.send(responses[index]) + elif type(responses[index]) is types.FunctionType: + callback_result = responses[index](locals()) + sys.stdout.flush() + if type(callback_result) is types.StringType: + child.send(callback_result) + elif callback_result: + break + else: + raise TypeError ('The callback must be a string or function type.') + event_count = event_count + 1 + except TIMEOUT, e: + child_result_list.append(child.before) + break + except EOF, e: + child_result_list.append(child.before) + break + child_result = ''.join(child_result_list) + if withexitstatus: + child.close() + return (child_result, child.exitstatus) + else: + return child_result + +class spawn (object): + """This is the main class interface for Pexpect. + Use this class to start and control child applications. + """ + + def __init__(self, command, args=[], timeout=30, maxread=2000, searchwindowsize=None, logfile=None, env=None): + """This is the constructor. The command parameter may be a string + that includes a command and any arguments to the command. For example: + p = pexpect.spawn ('/usr/bin/ftp') + p = pexpect.spawn ('/usr/bin/ssh user@example.com') + p = pexpect.spawn ('ls -latr /tmp') + You may also construct it with a list of arguments like so: + p = pexpect.spawn ('/usr/bin/ftp', []) + p = pexpect.spawn ('/usr/bin/ssh', ['user@example.com']) + p = pexpect.spawn ('ls', ['-latr', '/tmp']) + After this the child application will be created and + will be ready to talk to. For normal use, see expect() and + send() and sendline(). + + The maxread attribute sets the read buffer size. + This is maximum number of bytes that Pexpect will try to read + from a TTY at one time. + Seeting the maxread size to 1 will turn off buffering. + Setting the maxread value higher may help performance in cases + where large amounts of output are read back from the child. + This feature is useful in conjunction with searchwindowsize. + + The searchwindowsize attribute sets the how far back in + the incomming seach buffer Pexpect will search for pattern matches. + Every time Pexpect reads some data from the child it will append the data to + the incomming buffer. The default is to search from the beginning of the + imcomming buffer each time new data is read from the child. + But this is very inefficient if you are running a command that + generates a large amount of data where you want to match + The searchwindowsize does not effect the size of the incomming data buffer. + You will still have access to the full buffer after expect() returns. + + The logfile member turns on or off logging. + All input and output will be copied to the given file object. + Set logfile to None to stop logging. This is the default. + Set logfile to sys.stdout to echo everything to standard output. + The logfile is flushed after each write. + Example 1: + child = pexpect.spawn('some_command') + fout = file('mylog.txt','w') + child.logfile = fout + Example 2: + child = pexpect.spawn('some_command') + child.logfile = sys.stdout + + The delaybeforesend helps overcome a weird behavior that many users were experiencing. + The typical problem was that a user would expect() a "Password:" prompt and + then immediately call sendline() to send the password. The user would then + see that their password was echoed back to them. Passwords don't + normally echo. The problem is caused by the fact that most applications + print out the "Password" prompt and then turn off stdin echo, but if you + send your password before the application turned off echo, then you get + your password echoed. Normally this wouldn't be a problem when interacting + with a human at a real heyboard. If you introduce a slight delay just before + writing then this seems to clear up the problem. This was such a common problem + for many users that I decided that the default pexpect behavior + should be to sleep just before writing to the child application. + 1/10th of a second (100 ms) seems to be enough to clear up the problem. + You can set delaybeforesend to 0 to return to the old behavior. + + Note that spawn is clever about finding commands on your path. + It uses the same logic that "which" uses to find executables. + + If you wish to get the exit status of the child you must call + the close() method. The exit or signal status of the child will be + stored in self.exitstatus or self.signalstatus. + If the child exited normally then exitstatus will store the exit return code and + signalstatus will be None. + If the child was terminated abnormally with a signal then signalstatus will store + the signal value and exitstatus will be None. + If you need more detail you can also read the self.status member which stores + the status returned by os.waitpid. You can interpret this using + os.WIFEXITED/os.WEXITSTATUS or os.WIFSIGNALED/os.TERMSIG. + """ + self.STDIN_FILENO = pty.STDIN_FILENO + self.STDOUT_FILENO = pty.STDOUT_FILENO + self.STDERR_FILENO = pty.STDERR_FILENO + self.stdin = sys.stdin + self.stdout = sys.stdout + self.stderr = sys.stderr + + self.patterns = None + self.ignorecase = False + self.before = None + self.after = None + self.match = None + self.match_index = None + self.terminated = True + self.exitstatus = None + self.signalstatus = None + self.status = None # status returned by os.waitpid + self.flag_eof = False + self.pid = None + self.child_fd = -1 # initially closed + self.timeout = timeout + self.delimiter = EOF + self.logfile = logfile + self.maxread = maxread # Max bytes to read at one time into buffer. + self.buffer = '' # This is the read buffer. See maxread. + self.searchwindowsize = searchwindowsize # Anything before searchwindowsize point is preserved, but not searched. + self.delaybeforesend = 0.1 # Sets sleep time used just before sending data to child. + self.delayafterclose = 0.1 # Sets delay in close() method to allow kernel time to update process status. + self.delayafterterminate = 0.1 # Sets delay in terminate() method to allow kernel time to update process status. + self.softspace = False # File-like object. + self.name = '<' + repr(self) + '>' # File-like object. + self.encoding = None # File-like object. + self.closed = True # File-like object. + self.env = env + self.__irix_hack = sys.platform.lower().find('irix') >= 0 # This flags if we are running on irix + self.use_native_pty_fork = not (sys.platform.lower().find('solaris') >= 0) # Solaris uses internal __fork_pty(). All other use pty.fork(). + + # allow dummy instances for subclasses that may not use command or args. + if command is None: + self.command = None + self.args = None + self.name = '<pexpect factory incomplete>' + return + + # If command is an int type then it may represent a file descriptor. + if type(command) == type(0): + raise ExceptionPexpect ('Command is an int type. If this is a file descriptor then maybe you want to use fdpexpect.fdspawn which takes an existing file descriptor instead of a command string.') + + if type (args) != type([]): + raise TypeError ('The argument, args, must be a list.') + + if args == []: + self.args = split_command_line(command) + self.command = self.args[0] + else: + self.args = args[:] # work with a copy + self.args.insert (0, command) + self.command = command + + command_with_path = which(self.command) + if command_with_path is None: + raise ExceptionPexpect ('The command was not found or was not executable: %s.' % self.command) + self.command = command_with_path + self.args[0] = self.command + + self.name = '<' + ' '.join (self.args) + '>' + self.__spawn() + + def __del__(self): + """This makes sure that no system resources are left open. + Python only garbage collects Python objects. OS file descriptors + are not Python objects, so they must be handled explicitly. + If the child file descriptor was opened outside of this class + (passed to the constructor) then this does not close it. + """ + if not self.closed: + self.close() + + def __str__(self): + """This returns the current state of the pexpect object as a string. + """ + s = [] + s.append(repr(self)) + s.append('version: ' + __version__ + ' (' + __revision__ + ')') + s.append('command: ' + str(self.command)) + s.append('args: ' + str(self.args)) + if self.patterns is None: + s.append('patterns: None') + else: + s.append('patterns:') + for p in self.patterns: + if type(p) is type(re.compile('')): + s.append(' ' + str(p.pattern)) + else: + s.append(' ' + str(p)) + s.append('buffer (last 100 chars): ' + str(self.buffer)[-100:]) + s.append('before (last 100 chars): ' + str(self.before)[-100:]) + s.append('after: ' + str(self.after)) + s.append('match: ' + str(self.match)) + s.append('match_index: ' + str(self.match_index)) + s.append('exitstatus: ' + str(self.exitstatus)) + s.append('flag_eof: ' + str(self.flag_eof)) + s.append('pid: ' + str(self.pid)) + s.append('child_fd: ' + str(self.child_fd)) + s.append('closed: ' + str(self.closed)) + s.append('timeout: ' + str(self.timeout)) + s.append('delimiter: ' + str(self.delimiter)) + s.append('logfile: ' + str(self.logfile)) + s.append('maxread: ' + str(self.maxread)) + s.append('ignorecase: ' + str(self.ignorecase)) + s.append('searchwindowsize: ' + str(self.searchwindowsize)) + s.append('delaybeforesend: ' + str(self.delaybeforesend)) + s.append('delayafterclose: ' + str(self.delayafterclose)) + s.append('delayafterterminate: ' + str(self.delayafterterminate)) + return '\n'.join(s) + + def __spawn(self): + """This starts the given command in a child process. + This does all the fork/exec type of stuff for a pty. + This is called by __init__. + """ + # The pid and child_fd of this object get set by this method. + # Note that it is difficult for this method to fail. + # You cannot detect if the child process cannot start. + # So the only way you can tell if the child process started + # or not is to try to read from the file descriptor. If you get + # EOF immediately then it means that the child is already dead. + # That may not necessarily be bad because you may haved spawned a child + # that performs some task; creates no stdout output; and then dies. + + assert self.pid is None, 'The pid member should be None.' + assert self.command is not None, 'The command member should not be None.' + + if self.use_native_pty_fork: + try: + self.pid, self.child_fd = pty.fork() + except OSError, e: + raise ExceptionPexpect('Error! pty.fork() failed: ' + str(e)) + else: # Use internal __fork_pty + self.pid, self.child_fd = self.__fork_pty() + + if self.pid == 0: # Child + try: + self.child_fd = sys.stdout.fileno() # used by setwinsize() + #self.setwinsize(24, 80) + except: + ## Some platforms do not like setwinsize (Cygwin). + # This will cause problem when running applications that + # are very picky about window size. + # This is a serious limitation, but not a show stopper. + pass + # Do not allow child to inherit open file descriptors from parent. + max_fd = resource.getrlimit(resource.RLIMIT_NOFILE)[0] + for i in range (3, max_fd): + try: + os.close (i) + except OSError: + pass + + # I don't know why this works, but ignoring SIGHUP fixes a + # problem when trying to start a Java daemon with sudo + # (specifically, Tomcat). + signal.signal(signal.SIGHUP, signal.SIG_IGN) + + if self.env is None: + os.execv(self.command, self.args) + else: + os.execvpe(self.command, self.args, self.env) + + # Parent + self.terminated = False + self.closed = False + + def __fork_pty(self): + """This implements a substitute for the forkpty system call. + This should be more portable than the pty.fork() function. + Specifically, this should work on Solaris. + + Modified 10.06.05 by Geoff Marshall: + Implemented __fork_pty() method to resolve the issue with Python's + pty.fork() not supporting Solaris, particularly ssh. + Based on patch to posixmodule.c authored by Noah Spurrier: + http://mail.python.org/pipermail/python-dev/2003-May/035281.html + """ + parent_fd, child_fd = os.openpty() + if parent_fd < 0 or child_fd < 0: + raise ExceptionPexpect, "Error! Could not open pty with os.openpty()." + + pid = os.fork() + if pid < 0: + raise ExceptionPexpect, "Error! Failed os.fork()." + elif pid == 0: + # Child. + os.close(parent_fd) + self.__pty_make_controlling_tty(child_fd) + + os.dup2(child_fd, 0) + os.dup2(child_fd, 1) + os.dup2(child_fd, 2) + + if child_fd > 2: + os.close(child_fd) + else: + # Parent. + os.close(child_fd) + + return pid, parent_fd + + def __pty_make_controlling_tty(self, tty_fd): + """This makes the pseudo-terminal the controlling tty. + This should be more portable than the pty.fork() function. + Specifically, this should work on Solaris. + """ + child_name = os.ttyname(tty_fd) + + # Disconnect from controlling tty if still connected. + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY); + if fd >= 0: + os.close(fd) + + os.setsid() + + # Verify we are disconnected from controlling tty + try: + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY); + if fd >= 0: + os.close(fd) + raise ExceptionPexpect, "Error! We are not disconnected from a controlling tty." + except: + # Good! We are disconnected from a controlling tty. + pass + + # Verify we can open child pty. + fd = os.open(child_name, os.O_RDWR); + if fd < 0: + raise ExceptionPexpect, "Error! Could not open child pty, " + child_name + else: + os.close(fd) + + # Verify we now have a controlling tty. + fd = os.open("/dev/tty", os.O_WRONLY) + if fd < 0: + raise ExceptionPexpect, "Error! Could not open controlling tty, /dev/tty" + else: + os.close(fd) + + def fileno (self): # File-like object. + """This returns the file descriptor of the pty for the child. + """ + return self.child_fd + + def close (self, force=True): # File-like object. + """This closes the connection with the child application. + Note that calling close() more than once is valid. + This emulates standard Python behavior with files. + Set force to True if you want to make sure that the child is terminated + (SIGKILL is sent if the child ignores SIGHUP and SIGINT). + """ + if not self.closed: + self.flush() + os.close (self.child_fd) + self.child_fd = -1 + self.closed = True + time.sleep(self.delayafterclose) # Give kernel time to update process status. + if self.isalive(): + if not self.terminate(force): + raise ExceptionPexpect ('close() could not terminate the child using terminate()') + + def flush (self): # File-like object. + """This does nothing. It is here to support the interface for a File-like object. + """ + pass + + def isatty (self): # File-like object. + """This returns True if the file descriptor is open and connected to a tty(-like) device, else False. + """ + return os.isatty(self.child_fd) + + def setecho (self, state): + """This sets the terminal echo mode on or off. + Note that anything the child sent before the echo will be lost, so + you should be sure that your input buffer is empty before you setecho. + For example, the following will work as expected. + p = pexpect.spawn('cat') + p.sendline ('1234') # We will see this twice (once from tty echo and again from cat). + p.expect (['1234']) + p.expect (['1234']) + p.setecho(False) # Turn off tty echo + p.sendline ('abcd') # We will set this only once (echoed by cat). + p.sendline ('wxyz') # We will set this only once (echoed by cat) + p.expect (['abcd']) + p.expect (['wxyz']) + The following WILL NOT WORK because the lines sent before the setecho + will be lost: + p = pexpect.spawn('cat') + p.sendline ('1234') # We will see this twice (once from tty echo and again from cat). + p.setecho(False) # Turn off tty echo + p.sendline ('abcd') # We will set this only once (echoed by cat). + p.sendline ('wxyz') # We will set this only once (echoed by cat) + p.expect (['1234']) + p.expect (['1234']) + p.expect (['abcd']) + p.expect (['wxyz']) + """ + self.child_fd + new = termios.tcgetattr(self.child_fd) + if state: + new[3] = new[3] | termios.ECHO + else: + new[3] = new[3] & ~termios.ECHO + # I tried TCSADRAIN and TCSAFLUSH, but these were inconsistent + # and blocked on some platforms. TCSADRAIN is probably ideal if it worked. + termios.tcsetattr(self.child_fd, termios.TCSANOW, new) + + def read_nonblocking (self, size = 1, timeout = -1): + """This reads at most size characters from the child application. + It includes a timeout. If the read does not complete within the + timeout period then a TIMEOUT exception is raised. + If the end of file is read then an EOF exception will be raised. + If a log file was set using setlog() then all data will + also be written to the log file. + + If timeout==None then the read may block indefinitely. + If timeout==-1 then the self.timeout value is used. + If timeout==0 then the child is polled and + if there was no data immediately ready then this will raise a TIMEOUT exception. + + The "timeout" refers only to the amount of time to read at least one character. + This is not effected by the 'size' parameter, so if you call + read_nonblocking(size=100, timeout=30) and only one character is + available right away then one character will be returned immediately. + It will not wait for 30 seconds for another 99 characters to come in. + + This is a wrapper around os.read(). + It uses select.select() to implement a timeout. + """ + if self.closed: + raise ValueError ('I/O operation on closed file in read_nonblocking().') + + if timeout == -1: + timeout = self.timeout + + # Note that some systems such as Solaris do not give an EOF when + # the child dies. In fact, you can still try to read + # from the child_fd -- it will block forever or until TIMEOUT. + # For this case, I test isalive() before doing any reading. + # If isalive() is false, then I pretend that this is the same as EOF. + if not self.isalive(): + r,w,e = self.__select([self.child_fd], [], [], 0) # timeout of 0 means "poll" + if not r: + self.flag_eof = True + raise EOF ('End Of File (EOF) in read_nonblocking(). Braindead platform.') + elif self.__irix_hack: + # This is a hack for Irix. It seems that Irix requires a long delay before checking isalive. + # This adds a 2 second delay, but only when the child is terminated. + r, w, e = self.__select([self.child_fd], [], [], 2) + if not r and not self.isalive(): + self.flag_eof = True + raise EOF ('End Of File (EOF) in read_nonblocking(). Pokey platform.') + + r,w,e = self.__select([self.child_fd], [], [], timeout) + + if not r: + if not self.isalive(): + # Some platforms, such as Irix, will claim that their processes are alive; + # then timeout on the select; and then finally admit that they are not alive. + self.flag_eof = True + raise EOF ('End of File (EOF) in read_nonblocking(). Very pokey platform.') + else: + raise TIMEOUT ('Timeout exceeded in read_nonblocking().') + + if self.child_fd in r: + try: + s = os.read(self.child_fd, size) + except OSError, e: # Linux does this + self.flag_eof = True + raise EOF ('End Of File (EOF) in read_nonblocking(). Exception style platform.') + if s == '': # BSD style + self.flag_eof = True + raise EOF ('End Of File (EOF) in read_nonblocking(). Empty string style platform.') + + if self.logfile is not None: + self.logfile.write (s) + self.logfile.flush() + + return s + + raise ExceptionPexpect ('Reached an unexpected state in read_nonblocking().') + + def read (self, size = -1): # File-like object. + """This reads at most "size" bytes from the file + (less if the read hits EOF before obtaining size bytes). + If the size argument is negative or omitted, + read all data until EOF is reached. + The bytes are returned as a string object. + An empty string is returned when EOF is encountered immediately. + """ + if size == 0: + return '' + if size < 0: + self.expect (self.delimiter) # delimiter default is EOF + return self.before + + # I could have done this more directly by not using expect(), but + # I deliberately decided to couple read() to expect() so that + # I would catch any bugs early and ensure consistant behavior. + # It's a little less efficient, but there is less for me to + # worry about if I have to later modify read() or expect(). + # Note, it's OK if size==-1 in the regex. That just means it + # will never match anything in which case we stop only on EOF. + cre = re.compile('.{%d}' % size, re.DOTALL) + index = self.expect ([cre, self.delimiter]) # delimiter default is EOF + if index == 0: + return self.after ### self.before should be ''. Should I assert this? + return self.before + + def readline (self, size = -1): # File-like object. + """This reads and returns one entire line. A trailing newline is kept in + the string, but may be absent when a file ends with an incomplete line. + Note: This readline() looks for a \\r\\n pair even on UNIX because + this is what the pseudo tty device returns. So contrary to what you + may expect you will receive the newline as \\r\\n. + An empty string is returned when EOF is hit immediately. + Currently, the size agument is mostly ignored, so this behavior is not + standard for a file-like object. If size is 0 then an empty string + is returned. + """ + if size == 0: + return '' + index = self.expect (['\r\n', self.delimiter]) # delimiter default is EOF + if index == 0: + return self.before + '\r\n' + else: + return self.before + + def __iter__ (self): # File-like object. + """This is to support iterators over a file-like object. + """ + return self + + def next (self): # File-like object. + """This is to support iterators over a file-like object. + """ + result = self.readline() + if result == "": + raise StopIteration + return result + + def readlines (self, sizehint = -1): # File-like object. + """This reads until EOF using readline() and returns a list containing + the lines thus read. The optional "sizehint" argument is ignored. + """ + lines = [] + while True: + line = self.readline() + if not line: + break + lines.append(line) + return lines + + def write(self, str): # File-like object. + """This is similar to send() except that there is no return value. + """ + self.send (str) + + def writelines (self, sequence): # File-like object. + """This calls write() for each element in the sequence. + The sequence can be any iterable object producing strings, + typically a list of strings. This does not add line separators + There is no return value. + """ + for str in sequence: + self.write (str) + + def send(self, str): + """This sends a string to the child process. + This returns the number of bytes written. + If a log file was set then the data is also written to the log. + """ + time.sleep(self.delaybeforesend) + if self.logfile is not None: + self.logfile.write (str) + self.logfile.flush() + c = os.write(self.child_fd, str) + return c + + def sendline(self, str=''): + """This is like send(), but it adds a line feed (os.linesep). + This returns the number of bytes written. + """ + n = self.send(str) + n = n + self.send (os.linesep) + return n + + def sendeof(self): + """This sends an EOF to the child. + This sends a character which causes the pending parent output + buffer to be sent to the waiting child program without + waiting for end-of-line. If it is the first character of the + line, the read() in the user program returns 0, which + signifies end-of-file. This means to work as expected + a sendeof() has to be called at the begining of a line. + This method does not send a newline. It is the responsibility + of the caller to ensure the eof is sent at the beginning of a line. + """ + ### Hmmm... how do I send an EOF? + ###C if ((m = write(pty, *buf, p - *buf)) < 0) + ###C return (errno == EWOULDBLOCK) ? n : -1; + fd = sys.stdin.fileno() + old = termios.tcgetattr(fd) # remember current state + new = termios.tcgetattr(fd) + new[3] = new[3] | termios.ICANON # ICANON must be set to recognize EOF + try: # use try/finally to ensure state gets restored + termios.tcsetattr(fd, termios.TCSADRAIN, new) + if 'CEOF' in dir(termios): + os.write (self.child_fd, '%c' % termios.CEOF) + else: + os.write (self.child_fd, '%c' % 4) # Silly platform does not define CEOF so assume CTRL-D + finally: # restore state + termios.tcsetattr(fd, termios.TCSADRAIN, old) + + def eof (self): + """This returns True if the EOF exception was ever raised. + """ + return self.flag_eof + + def terminate(self, force=False): + """This forces a child process to terminate. + It starts nicely with SIGHUP and SIGINT. If "force" is True then + moves onto SIGKILL. + This returns True if the child was terminated. + This returns False if the child could not be terminated. + """ + if not self.isalive(): + return True + self.kill(signal.SIGHUP) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + self.kill(signal.SIGCONT) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + self.kill(signal.SIGINT) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + if force: + self.kill(signal.SIGKILL) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + else: + return False + return False + #raise ExceptionPexpect ('terminate() could not terminate child process. Try terminate(force=True)?') + + def wait(self): + """This waits until the child exits. This is a blocking call. + This will not read any data from the child, so this will block forever + if the child has unread output and has terminated. In other words, the child + may have printed output then called exit(); but, technically, the child is + still alive until its output is read. + """ + if self.isalive(): + pid, status = os.waitpid(self.pid, 0) + else: + raise ExceptionPexpect ('Cannot wait for dead child process.') + self.exitstatus = os.WEXITSTATUS(status) + if os.WIFEXITED (status): + self.status = status + self.exitstatus = os.WEXITSTATUS(status) + self.signalstatus = None + self.terminated = True + elif os.WIFSIGNALED (status): + self.status = status + self.exitstatus = None + self.signalstatus = os.WTERMSIG(status) + self.terminated = True + elif os.WIFSTOPPED (status): + raise ExceptionPexpect ('Wait was called for a child process that is stopped. This is not supported. Is some other process attempting job control with our child pid?') + return self.exitstatus + + def isalive(self): + """This tests if the child process is running or not. + This is non-blocking. If the child was terminated then this + will read the exitstatus or signalstatus of the child. + This returns True if the child process appears to be running or False if not. + It can take literally SECONDS for Solaris to return the right status. + """ + if self.terminated: + return False + + if self.flag_eof: + # This is for Linux, which requires the blocking form of waitpid to get + # status of a defunct process. This is super-lame. The flag_eof would have + # been set in read_nonblocking(), so this should be safe. + waitpid_options = 0 + else: + waitpid_options = os.WNOHANG + + try: + pid, status = os.waitpid(self.pid, waitpid_options) + except OSError, e: # No child processes + if e[0] == errno.ECHILD: + raise ExceptionPexpect ('isalive() encountered condition where "terminated" is 0, but there was no child process. Did someone else call waitpid() on our process?') + else: + raise e + + # I have to do this twice for Solaris. I can't even believe that I figured this out... + # If waitpid() returns 0 it means that no child process wishes to + # report, and the value of status is undefined. + if pid == 0: + try: + pid, status = os.waitpid(self.pid, waitpid_options) ### os.WNOHANG) # Solaris! + except OSError, e: # This should never happen... + if e[0] == errno.ECHILD: + raise ExceptionPexpect ('isalive() encountered condition that should never happen. There was no child process. Did someone else call waitpid() on our process?') + else: + raise e + + # If pid is still 0 after two calls to waitpid() then + # the process really is alive. This seems to work on all platforms, except + # for Irix which seems to require a blocking call on waitpid or select, so I let read_nonblocking + # take care of this situation (unfortunately, this requires waiting through the timeout). + if pid == 0: + return True + + if pid == 0: + return True + + if os.WIFEXITED (status): + self.status = status + self.exitstatus = os.WEXITSTATUS(status) + self.signalstatus = None + self.terminated = True + elif os.WIFSIGNALED (status): + self.status = status + self.exitstatus = None + self.signalstatus = os.WTERMSIG(status) + self.terminated = True + elif os.WIFSTOPPED (status): + raise ExceptionPexpect ('isalive() encountered condition where child process is stopped. This is not supported. Is some other process attempting job control with our child pid?') + return False + + def kill(self, sig): + """This sends the given signal to the child application. + In keeping with UNIX tradition it has a misleading name. + It does not necessarily kill the child unless + you send the right signal. + """ + # Same as os.kill, but the pid is given for you. + if self.isalive(): + os.kill(self.pid, sig) + + def compile_pattern_list(self, patterns): + """This compiles a pattern-string or a list of pattern-strings. + Patterns must be a StringType, EOF, TIMEOUT, SRE_Pattern, or + a list of those. Patterns may also be None which results in + an empty list. + + This is used by expect() when calling expect_list(). + Thus expect() is nothing more than:: + cpl = self.compile_pattern_list(pl) + return self.expect_list(clp, timeout) + + If you are using expect() within a loop it may be more + efficient to compile the patterns first and then call expect_list(). + This avoid calls in a loop to compile_pattern_list(): + cpl = self.compile_pattern_list(my_pattern) + while some_condition: + ... + i = self.expect_list(clp, timeout) + ... + """ + if patterns is None: + return [] + if type(patterns) is not types.ListType: + patterns = [patterns] + + compile_flags = re.DOTALL # Allow dot to match \n + if self.ignorecase: + compile_flags = compile_flags | re.IGNORECASE + compiled_pattern_list = [] + for p in patterns: + if type(p) is types.StringType: + compiled_pattern_list.append(re.compile(p, compile_flags)) + elif p is EOF: + compiled_pattern_list.append(EOF) + elif p is TIMEOUT: + compiled_pattern_list.append(TIMEOUT) + elif type(p) is type(re.compile('')): + compiled_pattern_list.append(p) + else: + raise TypeError ('Argument must be one of StringType, EOF, TIMEOUT, SRE_Pattern, or a list of those type. %s' % str(type(p))) + + return compiled_pattern_list + + def expect(self, pattern, timeout = -1, searchwindowsize=None): + + """This seeks through the stream until a pattern is matched. + The pattern is overloaded and may take several types including a list. + The pattern can be a StringType, EOF, a compiled re, or a list of + those types. Strings will be compiled to re types. This returns the + index into the pattern list. If the pattern was not a list this + returns index 0 on a successful match. This may raise exceptions for + EOF or TIMEOUT. To avoid the EOF or TIMEOUT exceptions add + EOF or TIMEOUT to the pattern list. + + After a match is found the instance attributes + 'before', 'after' and 'match' will be set. + You can see all the data read before the match in 'before'. + You can see the data that was matched in 'after'. + The re.MatchObject used in the re match will be in 'match'. + If an error occured then 'before' will be set to all the + data read so far and 'after' and 'match' will be None. + + If timeout is -1 then timeout will be set to the self.timeout value. + + Note: A list entry may be EOF or TIMEOUT instead of a string. + This will catch these exceptions and return the index + of the list entry instead of raising the exception. + The attribute 'after' will be set to the exception type. + The attribute 'match' will be None. + This allows you to write code like this: + index = p.expect (['good', 'bad', pexpect.EOF, pexpect.TIMEOUT]) + if index == 0: + do_something() + elif index == 1: + do_something_else() + elif index == 2: + do_some_other_thing() + elif index == 3: + do_something_completely_different() + instead of code like this: + try: + index = p.expect (['good', 'bad']) + if index == 0: + do_something() + elif index == 1: + do_something_else() + except EOF: + do_some_other_thing() + except TIMEOUT: + do_something_completely_different() + These two forms are equivalent. It all depends on what you want. + You can also just expect the EOF if you are waiting for all output + of a child to finish. For example: + p = pexpect.spawn('/bin/ls') + p.expect (pexpect.EOF) + print p.before + + If you are trying to optimize for speed then see expect_list(). + """ + compiled_pattern_list = self.compile_pattern_list(pattern) + return self.expect_list(compiled_pattern_list, timeout, searchwindowsize) + + def expect_list(self, pattern_list, timeout = -1, searchwindowsize = -1): + """This takes a list of compiled regular expressions and returns + the index into the pattern_list that matched the child output. + The list may also contain EOF or TIMEOUT (which are not + compiled regular expressions). This method is similar to + the expect() method except that expect_list() does not + recompile the pattern list on every call. + This may help if you are trying to optimize for speed, otherwise + just use the expect() method. This is called by expect(). + If timeout==-1 then the self.timeout value is used. + If searchwindowsize==-1 then the self.searchwindowsize value is used. + """ + + self.patterns = pattern_list + + if timeout == -1: + timeout = self.timeout + if timeout is not None: + end_time = time.time() + timeout + if searchwindowsize == -1: + searchwindowsize = self.searchwindowsize + + try: + incoming = self.buffer + while True: # Keep reading until exception or return. + # Sequence through the list of patterns looking for a match. + first_match = -1 + for cre in pattern_list: + if cre is EOF or cre is TIMEOUT: + continue # The patterns for PexpectExceptions are handled differently. + if searchwindowsize is None: # search everything + match = cre.search(incoming) + else: + startpos = max(0, len(incoming) - searchwindowsize) + match = cre.search(incoming, startpos) + if match is None: + continue + if first_match > match.start() or first_match == -1: + first_match = match.start() + self.match = match + self.match_index = pattern_list.index(cre) + if first_match > -1: + self.buffer = incoming[self.match.end() : ] + self.before = incoming[ : self.match.start()] + self.after = incoming[self.match.start() : self.match.end()] + return self.match_index + # No match at this point + if timeout < 0 and timeout is not None: + raise TIMEOUT ('Timeout exceeded in expect_list().') + # Still have time left, so read more data + c = self.read_nonblocking (self.maxread, timeout) + time.sleep (0.0001) + incoming = incoming + c + if timeout is not None: + timeout = end_time - time.time() + except EOF, e: + self.buffer = '' + self.before = incoming + self.after = EOF + if EOF in pattern_list: + self.match = EOF + self.match_index = pattern_list.index(EOF) + return self.match_index + else: + self.match = None + self.match_index = None + raise EOF (str(e) + '\n' + str(self)) + except TIMEOUT, e: + self.before = incoming + self.after = TIMEOUT + if TIMEOUT in pattern_list: + self.match = TIMEOUT + self.match_index = pattern_list.index(TIMEOUT) + return self.match_index + else: + self.match = None + self.match_index = None + raise TIMEOUT (str(e) + '\n' + str(self)) + except Exception: + self.before = incoming + self.after = None + self.match = None + self.match_index = None + raise + + def getwinsize(self): + """This returns the terminal window size of the child tty. + The return value is a tuple of (rows, cols). + """ + if 'TIOCGWINSZ' in dir(termios): + TIOCGWINSZ = termios.TIOCGWINSZ + else: + TIOCGWINSZ = 1074295912L # assume if not defined + s = struct.pack('HHHH', 0, 0, 0, 0) + x = fcntl.ioctl(self.fileno(), TIOCGWINSZ, s) + return struct.unpack('HHHH', x)[0:2] + + def setwinsize(self, r, c): + """This sets the terminal window size of the child tty. + This will cause a SIGWINCH signal to be sent to the child. + This does not change the physical window size. + It changes the size reported to TTY-aware applications like + vi or curses -- applications that respond to the SIGWINCH signal. + """ + # Check for buggy platforms. Some Python versions on some platforms + # (notably OSF1 Alpha and RedHat 7.1) truncate the value for + # termios.TIOCSWINSZ. It is not clear why this happens. + # These platforms don't seem to handle the signed int very well; + # yet other platforms like OpenBSD have a large negative value for + # TIOCSWINSZ and they don't have a truncate problem. + # Newer versions of Linux have totally different values for TIOCSWINSZ. + # Note that this fix is a hack. + if 'TIOCSWINSZ' in dir(termios): + TIOCSWINSZ = termios.TIOCSWINSZ + else: + TIOCSWINSZ = -2146929561 + if TIOCSWINSZ == 2148037735L: # L is not required in Python >= 2.2. + TIOCSWINSZ = -2146929561 # Same bits, but with sign. + # Note, assume ws_xpixel and ws_ypixel are zero. + s = struct.pack('HHHH', r, c, 0, 0) + fcntl.ioctl(self.fileno(), TIOCSWINSZ, s) + + def interact(self, escape_character = chr(29), input_filter = None, output_filter = None): + """This gives control of the child process to the interactive user + (the human at the keyboard). + Keystrokes are sent to the child process, and the stdout and stderr + output of the child process is printed. + This simply echos the child stdout and child stderr to the real + stdout and it echos the real stdin to the child stdin. + When the user types the escape_character this method will stop. + The default for escape_character is ^]. This should not be confused + with ASCII 27 -- the ESC character. ASCII 29 was chosen + for historical merit because this is the character used + by 'telnet' as the escape character. The escape_character will + not be sent to the child process. + + You may pass in optional input and output filter functions. + These functions should take a string and return a string. + The output_filter will be passed all the output from the child process. + The input_filter will be passed all the keyboard input from the user. + The input_filter is run BEFORE the check for the escape_character. + + Note that if you change the window size of the parent + the SIGWINCH signal will not be passed through to the child. + If you want the child window size to change when the parent's + window size changes then do something like the following example: + import pexpect, struct, fcntl, termios, signal, sys + def sigwinch_passthrough (sig, data): + s = struct.pack("HHHH", 0, 0, 0, 0) + a = struct.unpack('hhhh', fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ , s)) + global p + p.setwinsize(a[0],a[1]) + p = pexpect.spawn('/bin/bash') # Note this is global and used in sigwinch_passthrough. + signal.signal(signal.SIGWINCH, sigwinch_passthrough) + p.interact() + """ + # Flush the buffer. + self.stdout.write (self.buffer) + self.stdout.flush() + self.buffer = '' + mode = tty.tcgetattr(self.STDIN_FILENO) + tty.setraw(self.STDIN_FILENO) + try: + self.__interact_copy(escape_character, input_filter, output_filter) + finally: + tty.tcsetattr(self.STDIN_FILENO, tty.TCSAFLUSH, mode) + + def __interact_writen(self, fd, data): + """This is used by the interact() method. + """ + while data != '' and self.isalive(): + n = os.write(fd, data) + data = data[n:] + def __interact_read(self, fd): + """This is used by the interact() method. + """ + return os.read(fd, 1000) + def __interact_copy(self, escape_character = None, input_filter = None, output_filter = None): + """This is used by the interact() method. + """ + while self.isalive(): + r,w,e = self.__select([self.child_fd, self.STDIN_FILENO], [], []) + if self.child_fd in r: + data = self.__interact_read(self.child_fd) + if output_filter: data = output_filter(data) + if self.logfile is not None: + self.logfile.write (data) + self.logfile.flush() + os.write(self.STDOUT_FILENO, data) + if self.STDIN_FILENO in r: + data = self.__interact_read(self.STDIN_FILENO) + if input_filter: data = input_filter(data) + i = data.rfind(escape_character) + if i != -1: + data = data[:i] + self.__interact_writen(self.child_fd, data) + break + self.__interact_writen(self.child_fd, data) + def __select (self, iwtd, owtd, ewtd, timeout=None): + """This is a wrapper around select.select() that ignores signals. + If select.select raises a select.error exception and errno is an EINTR error then + it is ignored. Mainly this is used to ignore sigwinch (terminal resize). + """ + # if select() is interrupted by a signal (errno==EINTR) then + # we loop back and enter the select() again. + if timeout is not None: + end_time = time.time() + timeout + while True: + try: + return select.select (iwtd, owtd, ewtd, timeout) + except select.error, e: + if e[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 really is an exception + raise + +############################################################################## +# The following methods are no longer supported or allowed.. + def setmaxread (self, maxread): + """This method is no longer supported or allowed. + I don't like getters and setters without a good reason. + """ + raise ExceptionPexpect ('This method is no longer supported or allowed. Just assign a value to the maxread member variable.') + def expect_exact (self, pattern_list, timeout = -1): + """This method is no longer supported or allowed. + It was too hard to maintain and keep it up to date with expect_list. + Few people used this method. Most people favored reliability over speed. + The implementation is left in comments in case anyone needs to hack this + feature back into their copy. + If someone wants to diff this with expect_list and make them work + nearly the same then I will consider adding this make in. + """ + raise ExceptionPexpect ('This method is no longer supported or allowed.') + def setlog (self, fileobject): + """This method is no longer supported or allowed. + """ + raise ExceptionPexpect ('This method is no longer supported or allowed. Just assign a value to the logfile member variable.') + +############################################################################## +# End of spawn class +############################################################################## + +def which (filename): + """This takes a given filename; tries to find it in the environment path; + then checks if it is executable. + This returns the full path to the filename if found and executable. + Otherwise this returns None. + """ + # Special case where filename already contains a path. + if os.path.dirname(filename) != '': + if os.access (filename, os.X_OK): + return filename + + if not os.environ.has_key('PATH') or os.environ['PATH'] == '': + p = os.defpath + else: + p = os.environ['PATH'] + + # Oddly enough this was the one line that made Pexpect + # incompatible with Python 1.5.2. + #pathlist = p.split (os.pathsep) + pathlist = string.split (p, os.pathsep) + + for path in pathlist: + f = os.path.join(path, filename) + if os.access(f, os.X_OK): + return f + return None + +def split_command_line(command_line): + """This splits a command line into a list of arguments. + It splits arguments on spaces, but handles + embedded quotes, doublequotes, and escaped characters. + It's impossible to do this with a regular expression, so + I wrote a little state machine to parse the command line. + """ + arg_list = [] + arg = '' + + # Constants to name the states we can be in. + state_basic = 0 + state_esc = 1 + state_singlequote = 2 + state_doublequote = 3 + state_whitespace = 4 # The state of consuming whitespace between commands. + state = state_basic + + for c in command_line: + if state == state_basic or state == state_whitespace: + if c == '\\': # Escape the next character + state = state_esc + elif c == r"'": # Handle single quote + state = state_singlequote + elif c == r'"': # Handle double quote + state = state_doublequote + elif c.isspace(): + # Add arg to arg_list if we aren't in the middle of whitespace. + if state == state_whitespace: + None # Do nothing. + else: + arg_list.append(arg) + arg = '' + state = state_whitespace + else: + arg = arg + c + state = state_basic + elif state == state_esc: + arg = arg + c + state = state_basic + elif state == state_singlequote: + if c == r"'": + state = state_basic + else: + arg = arg + c + elif state == state_doublequote: + if c == r'"': + state = state_basic + else: + arg = arg + c + + if arg != '': + arg_list.append(arg) + return arg_list + |