##// END OF EJS Templates
Update irunner - needs work on pexpect to work in Python 3.
Thomas Kluyver -
Show More
@@ -1,440 +1,440 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Module for interactively running scripts.
2 """Module for interactively running scripts.
3
3
4 This module implements classes for interactively running scripts written for
4 This module implements classes for interactively running scripts written for
5 any system with a prompt which can be matched by a regexp suitable for
5 any system with a prompt which can be matched by a regexp suitable for
6 pexpect. It can be used to run as if they had been typed up interactively, an
6 pexpect. It can be used to run as if they had been typed up interactively, an
7 arbitrary series of commands for the target system.
7 arbitrary series of commands for the target system.
8
8
9 The module includes classes ready for IPython (with the default prompts),
9 The module includes classes ready for IPython (with the default prompts),
10 plain Python and SAGE, but making a new one is trivial. To see how to use it,
10 plain Python and SAGE, but making a new one is trivial. To see how to use it,
11 simply run the module as a script:
11 simply run the module as a script:
12
12
13 ./irunner.py --help
13 ./irunner.py --help
14
14
15
15
16 This is an extension of Ken Schutte <kschutte-AT-csail.mit.edu>'s script
16 This is an extension of Ken Schutte <kschutte-AT-csail.mit.edu>'s script
17 contributed on the ipython-user list:
17 contributed on the ipython-user list:
18
18
19 http://mail.scipy.org/pipermail/ipython-user/2006-May/003539.html
19 http://mail.scipy.org/pipermail/ipython-user/2006-May/003539.html
20
20
21
21
22 NOTES:
22 NOTES:
23
23
24 - This module requires pexpect, available in most linux distros, or which can
24 - This module requires pexpect, available in most linux distros, or which can
25 be downloaded from
25 be downloaded from
26
26
27 http://pexpect.sourceforge.net
27 http://pexpect.sourceforge.net
28
28
29 - Because pexpect only works under Unix or Windows-Cygwin, this has the same
29 - Because pexpect only works under Unix or Windows-Cygwin, this has the same
30 limitations. This means that it will NOT work under native windows Python.
30 limitations. This means that it will NOT work under native windows Python.
31 """
31 """
32
32
33 # Stdlib imports
33 # Stdlib imports
34 import optparse
34 import optparse
35 import os
35 import os
36 import sys
36 import sys
37
37
38 # Third-party modules: we carry a copy of pexpect to reduce the need for
38 # Third-party modules: we carry a copy of pexpect to reduce the need for
39 # external dependencies, but our import checks for a system version first.
39 # external dependencies, but our import checks for a system version first.
40 from IPython.external import pexpect
40 from IPython.external import pexpect
41
41
42 # Global usage strings, to avoid indentation issues when typing it below.
42 # Global usage strings, to avoid indentation issues when typing it below.
43 USAGE = """
43 USAGE = """
44 Interactive script runner, type: %s
44 Interactive script runner, type: %s
45
45
46 runner [opts] script_name
46 runner [opts] script_name
47 """
47 """
48
48
49 def pexpect_monkeypatch():
49 def pexpect_monkeypatch():
50 """Patch pexpect to prevent unhandled exceptions at VM teardown.
50 """Patch pexpect to prevent unhandled exceptions at VM teardown.
51
51
52 Calling this function will monkeypatch the pexpect.spawn class and modify
52 Calling this function will monkeypatch the pexpect.spawn class and modify
53 its __del__ method to make it more robust in the face of failures that can
53 its __del__ method to make it more robust in the face of failures that can
54 occur if it is called when the Python VM is shutting down.
54 occur if it is called when the Python VM is shutting down.
55
55
56 Since Python may fire __del__ methods arbitrarily late, it's possible for
56 Since Python may fire __del__ methods arbitrarily late, it's possible for
57 them to execute during the teardown of the Python VM itself. At this
57 them to execute during the teardown of the Python VM itself. At this
58 point, various builtin modules have been reset to None. Thus, the call to
58 point, various builtin modules have been reset to None. Thus, the call to
59 self.close() will trigger an exception because it tries to call os.close(),
59 self.close() will trigger an exception because it tries to call os.close(),
60 and os is now None.
60 and os is now None.
61 """
61 """
62
62
63 if pexpect.__version__[:3] >= '2.2':
63 if pexpect.__version__[:3] >= '2.2':
64 # No need to patch, fix is already the upstream version.
64 # No need to patch, fix is already the upstream version.
65 return
65 return
66
66
67 def __del__(self):
67 def __del__(self):
68 """This makes sure that no system resources are left open.
68 """This makes sure that no system resources are left open.
69 Python only garbage collects Python objects. OS file descriptors
69 Python only garbage collects Python objects. OS file descriptors
70 are not Python objects, so they must be handled explicitly.
70 are not Python objects, so they must be handled explicitly.
71 If the child file descriptor was opened outside of this class
71 If the child file descriptor was opened outside of this class
72 (passed to the constructor) then this does not close it.
72 (passed to the constructor) then this does not close it.
73 """
73 """
74 if not self.closed:
74 if not self.closed:
75 try:
75 try:
76 self.close()
76 self.close()
77 except AttributeError:
77 except AttributeError:
78 pass
78 pass
79
79
80 pexpect.spawn.__del__ = __del__
80 pexpect.spawn.__del__ = __del__
81
81
82 pexpect_monkeypatch()
82 pexpect_monkeypatch()
83
83
84 # The generic runner class
84 # The generic runner class
85 class InteractiveRunner(object):
85 class InteractiveRunner(object):
86 """Class to run a sequence of commands through an interactive program."""
86 """Class to run a sequence of commands through an interactive program."""
87
87
88 def __init__(self,program,prompts,args=None,out=sys.stdout,echo=True):
88 def __init__(self,program,prompts,args=None,out=sys.stdout,echo=True):
89 """Construct a runner.
89 """Construct a runner.
90
90
91 Inputs:
91 Inputs:
92
92
93 - program: command to execute the given program.
93 - program: command to execute the given program.
94
94
95 - prompts: a list of patterns to match as valid prompts, in the
95 - prompts: a list of patterns to match as valid prompts, in the
96 format used by pexpect. This basically means that it can be either
96 format used by pexpect. This basically means that it can be either
97 a string (to be compiled as a regular expression) or a list of such
97 a string (to be compiled as a regular expression) or a list of such
98 (it must be a true list, as pexpect does type checks).
98 (it must be a true list, as pexpect does type checks).
99
99
100 If more than one prompt is given, the first is treated as the main
100 If more than one prompt is given, the first is treated as the main
101 program prompt and the others as 'continuation' prompts, like
101 program prompt and the others as 'continuation' prompts, like
102 python's. This means that blank lines in the input source are
102 python's. This means that blank lines in the input source are
103 ommitted when the first prompt is matched, but are NOT ommitted when
103 ommitted when the first prompt is matched, but are NOT ommitted when
104 the continuation one matches, since this is how python signals the
104 the continuation one matches, since this is how python signals the
105 end of multiline input interactively.
105 end of multiline input interactively.
106
106
107 Optional inputs:
107 Optional inputs:
108
108
109 - args(None): optional list of strings to pass as arguments to the
109 - args(None): optional list of strings to pass as arguments to the
110 child program.
110 child program.
111
111
112 - out(sys.stdout): if given, an output stream to be used when writing
112 - out(sys.stdout): if given, an output stream to be used when writing
113 output. The only requirement is that it must have a .write() method.
113 output. The only requirement is that it must have a .write() method.
114
114
115 Public members not parameterized in the constructor:
115 Public members not parameterized in the constructor:
116
116
117 - delaybeforesend(0): Newer versions of pexpect have a delay before
117 - delaybeforesend(0): Newer versions of pexpect have a delay before
118 sending each new input. For our purposes here, it's typically best
118 sending each new input. For our purposes here, it's typically best
119 to just set this to zero, but if you encounter reliability problems
119 to just set this to zero, but if you encounter reliability problems
120 or want an interactive run to pause briefly at each prompt, just
120 or want an interactive run to pause briefly at each prompt, just
121 increase this value (it is measured in seconds). Note that this
121 increase this value (it is measured in seconds). Note that this
122 variable is not honored at all by older versions of pexpect.
122 variable is not honored at all by older versions of pexpect.
123 """
123 """
124
124
125 self.program = program
125 self.program = program
126 self.prompts = prompts
126 self.prompts = prompts
127 if args is None: args = []
127 if args is None: args = []
128 self.args = args
128 self.args = args
129 self.out = out
129 self.out = out
130 self.echo = echo
130 self.echo = echo
131 # Other public members which we don't make as parameters, but which
131 # Other public members which we don't make as parameters, but which
132 # users may occasionally want to tweak
132 # users may occasionally want to tweak
133 self.delaybeforesend = 0
133 self.delaybeforesend = 0
134
134
135 # Create child process and hold on to it so we don't have to re-create
135 # Create child process and hold on to it so we don't have to re-create
136 # for every single execution call
136 # for every single execution call
137 c = self.child = pexpect.spawn(self.program,self.args,timeout=None)
137 c = self.child = pexpect.spawn(self.program,self.args,timeout=None)
138 c.delaybeforesend = self.delaybeforesend
138 c.delaybeforesend = self.delaybeforesend
139 # pexpect hard-codes the terminal size as (24,80) (rows,columns).
139 # pexpect hard-codes the terminal size as (24,80) (rows,columns).
140 # This causes problems because any line longer than 80 characters gets
140 # This causes problems because any line longer than 80 characters gets
141 # completely overwrapped on the printed outptut (even though
141 # completely overwrapped on the printed outptut (even though
142 # internally the code runs fine). We reset this to 99 rows X 200
142 # internally the code runs fine). We reset this to 99 rows X 200
143 # columns (arbitrarily chosen), which should avoid problems in all
143 # columns (arbitrarily chosen), which should avoid problems in all
144 # reasonable cases.
144 # reasonable cases.
145 c.setwinsize(99,200)
145 c.setwinsize(99,200)
146
146
147 def close(self):
147 def close(self):
148 """close child process"""
148 """close child process"""
149
149
150 self.child.close()
150 self.child.close()
151
151
152 def run_file(self,fname,interact=False,get_output=False):
152 def run_file(self,fname,interact=False,get_output=False):
153 """Run the given file interactively.
153 """Run the given file interactively.
154
154
155 Inputs:
155 Inputs:
156
156
157 -fname: name of the file to execute.
157 -fname: name of the file to execute.
158
158
159 See the run_source docstring for the meaning of the optional
159 See the run_source docstring for the meaning of the optional
160 arguments."""
160 arguments."""
161
161
162 fobj = open(fname,'r')
162 fobj = open(fname,'r')
163 try:
163 try:
164 out = self.run_source(fobj,interact,get_output)
164 out = self.run_source(fobj,interact,get_output)
165 finally:
165 finally:
166 fobj.close()
166 fobj.close()
167 if get_output:
167 if get_output:
168 return out
168 return out
169
169
170 def run_source(self,source,interact=False,get_output=False):
170 def run_source(self,source,interact=False,get_output=False):
171 """Run the given source code interactively.
171 """Run the given source code interactively.
172
172
173 Inputs:
173 Inputs:
174
174
175 - source: a string of code to be executed, or an open file object we
175 - source: a string of code to be executed, or an open file object we
176 can iterate over.
176 can iterate over.
177
177
178 Optional inputs:
178 Optional inputs:
179
179
180 - interact(False): if true, start to interact with the running
180 - interact(False): if true, start to interact with the running
181 program at the end of the script. Otherwise, just exit.
181 program at the end of the script. Otherwise, just exit.
182
182
183 - get_output(False): if true, capture the output of the child process
183 - get_output(False): if true, capture the output of the child process
184 (filtering the input commands out) and return it as a string.
184 (filtering the input commands out) and return it as a string.
185
185
186 Returns:
186 Returns:
187 A string containing the process output, but only if requested.
187 A string containing the process output, but only if requested.
188 """
188 """
189
189
190 # if the source is a string, chop it up in lines so we can iterate
190 # if the source is a string, chop it up in lines so we can iterate
191 # over it just as if it were an open file.
191 # over it just as if it were an open file.
192 if not isinstance(source,file):
192 if isinstance(source, basestring):
193 source = source.splitlines(True)
193 source = source.splitlines(True)
194
194
195 if self.echo:
195 if self.echo:
196 # normalize all strings we write to use the native OS line
196 # normalize all strings we write to use the native OS line
197 # separators.
197 # separators.
198 linesep = os.linesep
198 linesep = os.linesep
199 stdwrite = self.out.write
199 stdwrite = self.out.write
200 write = lambda s: stdwrite(s.replace('\r\n',linesep))
200 write = lambda s: stdwrite(s.replace('\r\n',linesep))
201 else:
201 else:
202 # Quiet mode, all writes are no-ops
202 # Quiet mode, all writes are no-ops
203 write = lambda s: None
203 write = lambda s: None
204
204
205 c = self.child
205 c = self.child
206 prompts = c.compile_pattern_list(self.prompts)
206 prompts = c.compile_pattern_list(self.prompts)
207 prompt_idx = c.expect_list(prompts)
207 prompt_idx = c.expect_list(prompts)
208
208
209 # Flag whether the script ends normally or not, to know whether we can
209 # Flag whether the script ends normally or not, to know whether we can
210 # do anything further with the underlying process.
210 # do anything further with the underlying process.
211 end_normal = True
211 end_normal = True
212
212
213 # If the output was requested, store it in a list for return at the end
213 # If the output was requested, store it in a list for return at the end
214 if get_output:
214 if get_output:
215 output = []
215 output = []
216 store_output = output.append
216 store_output = output.append
217
217
218 for cmd in source:
218 for cmd in source:
219 # skip blank lines for all matches to the 'main' prompt, while the
219 # skip blank lines for all matches to the 'main' prompt, while the
220 # secondary prompts do not
220 # secondary prompts do not
221 if prompt_idx==0 and \
221 if prompt_idx==0 and \
222 (cmd.isspace() or cmd.lstrip().startswith('#')):
222 (cmd.isspace() or cmd.lstrip().startswith('#')):
223 write(cmd)
223 write(cmd)
224 continue
224 continue
225
225
226 # write('AFTER: '+c.after) # dbg
226 # write('AFTER: '+c.after) # dbg
227 write(c.after)
227 write(c.after)
228 c.send(cmd)
228 c.send(cmd)
229 try:
229 try:
230 prompt_idx = c.expect_list(prompts)
230 prompt_idx = c.expect_list(prompts)
231 except pexpect.EOF:
231 except pexpect.EOF:
232 # this will happen if the child dies unexpectedly
232 # this will happen if the child dies unexpectedly
233 write(c.before)
233 write(c.before)
234 end_normal = False
234 end_normal = False
235 break
235 break
236
236
237 write(c.before)
237 write(c.before)
238
238
239 # With an echoing process, the output we get in c.before contains
239 # With an echoing process, the output we get in c.before contains
240 # the command sent, a newline, and then the actual process output
240 # the command sent, a newline, and then the actual process output
241 if get_output:
241 if get_output:
242 store_output(c.before[len(cmd+'\n'):])
242 store_output(c.before[len(cmd+'\n'):])
243 #write('CMD: <<%s>>' % cmd) # dbg
243 #write('CMD: <<%s>>' % cmd) # dbg
244 #write('OUTPUT: <<%s>>' % output[-1]) # dbg
244 #write('OUTPUT: <<%s>>' % output[-1]) # dbg
245
245
246 self.out.flush()
246 self.out.flush()
247 if end_normal:
247 if end_normal:
248 if interact:
248 if interact:
249 c.send('\n')
249 c.send('\n')
250 print '<< Starting interactive mode >>',
250 print '<< Starting interactive mode >>',
251 try:
251 try:
252 c.interact()
252 c.interact()
253 except OSError:
253 except OSError:
254 # This is what fires when the child stops. Simply print a
254 # This is what fires when the child stops. Simply print a
255 # newline so the system prompt is aligned. The extra
255 # newline so the system prompt is aligned. The extra
256 # space is there to make sure it gets printed, otherwise
256 # space is there to make sure it gets printed, otherwise
257 # OS buffering sometimes just suppresses it.
257 # OS buffering sometimes just suppresses it.
258 write(' \n')
258 write(' \n')
259 self.out.flush()
259 self.out.flush()
260 else:
260 else:
261 if interact:
261 if interact:
262 e="Further interaction is not possible: child process is dead."
262 e="Further interaction is not possible: child process is dead."
263 print >> sys.stderr, e
263 print >> sys.stderr, e
264
264
265 # Leave the child ready for more input later on, otherwise select just
265 # Leave the child ready for more input later on, otherwise select just
266 # hangs on the second invocation.
266 # hangs on the second invocation.
267 if c.isalive():
267 if c.isalive():
268 c.send('\n')
268 c.send('\n')
269
269
270 # Return any requested output
270 # Return any requested output
271 if get_output:
271 if get_output:
272 return ''.join(output)
272 return ''.join(output)
273
273
274 def main(self,argv=None):
274 def main(self,argv=None):
275 """Run as a command-line script."""
275 """Run as a command-line script."""
276
276
277 parser = optparse.OptionParser(usage=USAGE % self.__class__.__name__)
277 parser = optparse.OptionParser(usage=USAGE % self.__class__.__name__)
278 newopt = parser.add_option
278 newopt = parser.add_option
279 newopt('-i','--interact',action='store_true',default=False,
279 newopt('-i','--interact',action='store_true',default=False,
280 help='Interact with the program after the script is run.')
280 help='Interact with the program after the script is run.')
281
281
282 opts,args = parser.parse_args(argv)
282 opts,args = parser.parse_args(argv)
283
283
284 if len(args) != 1:
284 if len(args) != 1:
285 print >> sys.stderr,"You must supply exactly one file to run."
285 print >> sys.stderr,"You must supply exactly one file to run."
286 sys.exit(1)
286 sys.exit(1)
287
287
288 self.run_file(args[0],opts.interact)
288 self.run_file(args[0],opts.interact)
289
289
290
290
291 # Specific runners for particular programs
291 # Specific runners for particular programs
292 class IPythonRunner(InteractiveRunner):
292 class IPythonRunner(InteractiveRunner):
293 """Interactive IPython runner.
293 """Interactive IPython runner.
294
294
295 This initalizes IPython in 'nocolor' mode for simplicity. This lets us
295 This initalizes IPython in 'nocolor' mode for simplicity. This lets us
296 avoid having to write a regexp that matches ANSI sequences, though pexpect
296 avoid having to write a regexp that matches ANSI sequences, though pexpect
297 does support them. If anyone contributes patches for ANSI color support,
297 does support them. If anyone contributes patches for ANSI color support,
298 they will be welcome.
298 they will be welcome.
299
299
300 It also sets the prompts manually, since the prompt regexps for
300 It also sets the prompts manually, since the prompt regexps for
301 pexpect need to be matched to the actual prompts, so user-customized
301 pexpect need to be matched to the actual prompts, so user-customized
302 prompts would break this.
302 prompts would break this.
303 """
303 """
304
304
305 def __init__(self,program = 'ipython',args=None,out=sys.stdout,echo=True):
305 def __init__(self,program = 'ipython',args=None,out=sys.stdout,echo=True):
306 """New runner, optionally passing the ipython command to use."""
306 """New runner, optionally passing the ipython command to use."""
307 args0 = ['--colors=NoColor',
307 args0 = ['--colors=NoColor',
308 '--no-term-title',
308 '--no-term-title',
309 '--no-autoindent',
309 '--no-autoindent',
310 # '--quick' is important, to prevent loading default config:
310 # '--quick' is important, to prevent loading default config:
311 '--quick']
311 '--quick']
312 if args is None: args = args0
312 if args is None: args = args0
313 else: args = args0 + args
313 else: args = args0 + args
314 prompts = [r'In \[\d+\]: ',r' \.*: ']
314 prompts = [r'In \[\d+\]: ',r' \.*: ']
315 InteractiveRunner.__init__(self,program,prompts,args,out,echo)
315 InteractiveRunner.__init__(self,program,prompts,args,out,echo)
316
316
317
317
318 class PythonRunner(InteractiveRunner):
318 class PythonRunner(InteractiveRunner):
319 """Interactive Python runner."""
319 """Interactive Python runner."""
320
320
321 def __init__(self,program='python',args=None,out=sys.stdout,echo=True):
321 def __init__(self,program='python',args=None,out=sys.stdout,echo=True):
322 """New runner, optionally passing the python command to use."""
322 """New runner, optionally passing the python command to use."""
323
323
324 prompts = [r'>>> ',r'\.\.\. ']
324 prompts = [r'>>> ',r'\.\.\. ']
325 InteractiveRunner.__init__(self,program,prompts,args,out,echo)
325 InteractiveRunner.__init__(self,program,prompts,args,out,echo)
326
326
327
327
328 class SAGERunner(InteractiveRunner):
328 class SAGERunner(InteractiveRunner):
329 """Interactive SAGE runner.
329 """Interactive SAGE runner.
330
330
331 WARNING: this runner only works if you manually adjust your SAGE
331 WARNING: this runner only works if you manually adjust your SAGE
332 configuration so that the 'color' option in the configuration file is set to
332 configuration so that the 'color' option in the configuration file is set to
333 'NoColor', because currently the prompt matching regexp does not identify
333 'NoColor', because currently the prompt matching regexp does not identify
334 color sequences."""
334 color sequences."""
335
335
336 def __init__(self,program='sage',args=None,out=sys.stdout,echo=True):
336 def __init__(self,program='sage',args=None,out=sys.stdout,echo=True):
337 """New runner, optionally passing the sage command to use."""
337 """New runner, optionally passing the sage command to use."""
338
338
339 prompts = ['sage: ',r'\s*\.\.\. ']
339 prompts = ['sage: ',r'\s*\.\.\. ']
340 InteractiveRunner.__init__(self,program,prompts,args,out,echo)
340 InteractiveRunner.__init__(self,program,prompts,args,out,echo)
341
341
342
342
343 class RunnerFactory(object):
343 class RunnerFactory(object):
344 """Code runner factory.
344 """Code runner factory.
345
345
346 This class provides an IPython code runner, but enforces that only one
346 This class provides an IPython code runner, but enforces that only one
347 runner is ever instantiated. The runner is created based on the extension
347 runner is ever instantiated. The runner is created based on the extension
348 of the first file to run, and it raises an exception if a runner is later
348 of the first file to run, and it raises an exception if a runner is later
349 requested for a different extension type.
349 requested for a different extension type.
350
350
351 This ensures that we don't generate example files for doctest with a mix of
351 This ensures that we don't generate example files for doctest with a mix of
352 python and ipython syntax.
352 python and ipython syntax.
353 """
353 """
354
354
355 def __init__(self,out=sys.stdout):
355 def __init__(self,out=sys.stdout):
356 """Instantiate a code runner."""
356 """Instantiate a code runner."""
357
357
358 self.out = out
358 self.out = out
359 self.runner = None
359 self.runner = None
360 self.runnerClass = None
360 self.runnerClass = None
361
361
362 def _makeRunner(self,runnerClass):
362 def _makeRunner(self,runnerClass):
363 self.runnerClass = runnerClass
363 self.runnerClass = runnerClass
364 self.runner = runnerClass(out=self.out)
364 self.runner = runnerClass(out=self.out)
365 return self.runner
365 return self.runner
366
366
367 def __call__(self,fname):
367 def __call__(self,fname):
368 """Return a runner for the given filename."""
368 """Return a runner for the given filename."""
369
369
370 if fname.endswith('.py'):
370 if fname.endswith('.py'):
371 runnerClass = PythonRunner
371 runnerClass = PythonRunner
372 elif fname.endswith('.ipy'):
372 elif fname.endswith('.ipy'):
373 runnerClass = IPythonRunner
373 runnerClass = IPythonRunner
374 else:
374 else:
375 raise ValueError('Unknown file type for Runner: %r' % fname)
375 raise ValueError('Unknown file type for Runner: %r' % fname)
376
376
377 if self.runner is None:
377 if self.runner is None:
378 return self._makeRunner(runnerClass)
378 return self._makeRunner(runnerClass)
379 else:
379 else:
380 if runnerClass==self.runnerClass:
380 if runnerClass==self.runnerClass:
381 return self.runner
381 return self.runner
382 else:
382 else:
383 e='A runner of type %r can not run file %r' % \
383 e='A runner of type %r can not run file %r' % \
384 (self.runnerClass,fname)
384 (self.runnerClass,fname)
385 raise ValueError(e)
385 raise ValueError(e)
386
386
387
387
388 # Global usage string, to avoid indentation issues if typed in a function def.
388 # Global usage string, to avoid indentation issues if typed in a function def.
389 MAIN_USAGE = """
389 MAIN_USAGE = """
390 %prog [options] file_to_run
390 %prog [options] file_to_run
391
391
392 This is an interface to the various interactive runners available in this
392 This is an interface to the various interactive runners available in this
393 module. If you want to pass specific options to one of the runners, you need
393 module. If you want to pass specific options to one of the runners, you need
394 to first terminate the main options with a '--', and then provide the runner's
394 to first terminate the main options with a '--', and then provide the runner's
395 options. For example:
395 options. For example:
396
396
397 irunner.py --python -- --help
397 irunner.py --python -- --help
398
398
399 will pass --help to the python runner. Similarly,
399 will pass --help to the python runner. Similarly,
400
400
401 irunner.py --ipython -- --interact script.ipy
401 irunner.py --ipython -- --interact script.ipy
402
402
403 will run the script.ipy file under the IPython runner, and then will start to
403 will run the script.ipy file under the IPython runner, and then will start to
404 interact with IPython at the end of the script (instead of exiting).
404 interact with IPython at the end of the script (instead of exiting).
405
405
406 The already implemented runners are listed below; adding one for a new program
406 The already implemented runners are listed below; adding one for a new program
407 is a trivial task, see the source for examples.
407 is a trivial task, see the source for examples.
408 """
408 """
409
409
410 def main():
410 def main():
411 """Run as a command-line script."""
411 """Run as a command-line script."""
412
412
413 parser = optparse.OptionParser(usage=MAIN_USAGE)
413 parser = optparse.OptionParser(usage=MAIN_USAGE)
414 newopt = parser.add_option
414 newopt = parser.add_option
415 newopt('--ipython',action='store_const',dest='mode',const='ipython',
415 newopt('--ipython',action='store_const',dest='mode',const='ipython',
416 help='IPython interactive runner (default).')
416 help='IPython interactive runner (default).')
417 newopt('--python',action='store_const',dest='mode',const='python',
417 newopt('--python',action='store_const',dest='mode',const='python',
418 help='Python interactive runner.')
418 help='Python interactive runner.')
419 newopt('--sage',action='store_const',dest='mode',const='sage',
419 newopt('--sage',action='store_const',dest='mode',const='sage',
420 help='SAGE interactive runner.')
420 help='SAGE interactive runner.')
421
421
422 opts,args = parser.parse_args()
422 opts,args = parser.parse_args()
423 runners = dict(ipython=IPythonRunner,
423 runners = dict(ipython=IPythonRunner,
424 python=PythonRunner,
424 python=PythonRunner,
425 sage=SAGERunner)
425 sage=SAGERunner)
426
426
427 try:
427 try:
428 ext = os.path.splitext(args[0])[-1]
428 ext = os.path.splitext(args[0])[-1]
429 except IndexError:
429 except IndexError:
430 ext = ''
430 ext = ''
431 modes = {'.ipy':'ipython',
431 modes = {'.ipy':'ipython',
432 '.py':'python',
432 '.py':'python',
433 '.sage':'sage'}
433 '.sage':'sage'}
434 mode = modes.get(ext,"ipython")
434 mode = modes.get(ext,"ipython")
435 if opts.mode:
435 if opts.mode:
436 mode = opts.mode
436 mode = opts.mode
437 runners[mode]().main(args)
437 runners[mode]().main(args)
438
438
439 if __name__ == '__main__':
439 if __name__ == '__main__':
440 main()
440 main()
@@ -1,14 +1,18 b''
1 import os.path
1 import os.path
2 from setuptools import setup
2 from setuptools import setup
3
3
4 from setupbase import (setup_args, find_scripts, find_packages, find_package_data)
4 from setupbase import (setup_args,
5 find_scripts,
6 find_packages,
7 find_package_data,
8 )
5
9
6 setup_args['entry_points'] = find_scripts(True, suffix='3')
10 setup_args['entry_points'] = find_scripts(True, suffix='3')
7 setup_args['packages'] = find_packages()
11 setup_args['packages'] = find_packages()
8 setup_args['package_data'] = find_package_data()
12 setup_args['package_data'] = find_package_data()
9
13
10 def main():
14 def main():
11 setup(use_2to3 = True, **setup_args)
15 setup(use_2to3 = True, **setup_args)
12
16
13 if __name__ == "__main__":
17 if __name__ == "__main__":
14 main()
18 main()
General Comments 0
You need to be logged in to leave comments. Login now