[pypy-svn] pypy commit 5e8eb0237e7f: Whack at app_main until I get a result that I'm kind of happy with.
Bitbucket
commits-noreply at bitbucket.org
Mon Dec 13 17:58:23 CET 2010
# HG changeset patch -- Bitbucket.org
# Project pypy
# URL http://bitbucket.org/pypy/pypy/overview
# User Armin Rigo <arigo at tunes.org>
# Date 1292256686 0
# Node ID 5e8eb0237e7f098cfa76aa48cbc06aa44da7fceb
# Parent 14922678ff991b78f469128b2e0ffc819897fb94
Whack at app_main until I get a result that I'm kind of happy with.
Add tests for parse_command_line().
--- a/pypy/translator/goal/app_main.py
+++ b/pypy/translator/goal/app_main.py
@@ -22,6 +22,25 @@ DEBUG = False # dump exceptions be
originalexcepthook = sys.__excepthook__
+def handle_sys_exit(e):
+ # exit if we catch a w_SystemExit
+ exitcode = e.code
+ if exitcode is None:
+ exitcode = 0
+ else:
+ try:
+ exitcode = int(exitcode)
+ except:
+ # not an integer: print it to stderr
+ try:
+ stderr = sys.stderr
+ except AttributeError:
+ pass # too bad
+ else:
+ print >> stderr, exitcode
+ exitcode = 1
+ raise SystemExit(exitcode)
+
def run_toplevel(f, *fargs, **fkwds):
"""Calls f() and handles all OperationErrors.
Intended use is to run the main program or one interactive statement.
@@ -45,72 +64,58 @@ def run_toplevel(f, *fargs, **fkwds):
stdout.write('\n')
except SystemExit, e:
- # exit if we catch a w_SystemExit
- exitcode = e.code
- if exitcode is None:
- exitcode = 0
- else:
- try:
- exitcode = int(exitcode)
- except:
- # not an integer: print it to stderr
- try:
- stderr = sys.stderr
- except AttributeError:
- pass # too bad
- else:
- print >> stderr, exitcode
- exitcode = 1
- raise SystemExit(exitcode)
+ handle_sys_exit(e)
+ except:
+ display_exception()
+ return False
+ return True # success
+
+def display_exception():
+ etype, evalue, etraceback = sys.exc_info()
+ try:
+ # extra debugging info in case the code below goes very wrong
+ if DEBUG and hasattr(sys, 'stderr'):
+ s = getattr(etype, '__name__', repr(etype))
+ print >> sys.stderr, "debug: exception-type: ", s
+ print >> sys.stderr, "debug: exception-value:", str(evalue)
+ tbentry = etraceback
+ if tbentry:
+ while tbentry.tb_next:
+ tbentry = tbentry.tb_next
+ lineno = tbentry.tb_lineno
+ filename = tbentry.tb_frame.f_code.co_filename
+ print >> sys.stderr, "debug: exception-tb: %s:%d" % (
+ filename, lineno)
+
+ # set the sys.last_xxx attributes
+ sys.last_type = etype
+ sys.last_value = evalue
+ sys.last_traceback = etraceback
+
+ # call sys.excepthook
+ hook = getattr(sys, 'excepthook', originalexcepthook)
+ hook(etype, evalue, etraceback)
+ return # done
except:
- etype, evalue, etraceback = sys.exc_info()
try:
- # extra debugging info in case the code below goes very wrong
- if DEBUG and hasattr(sys, 'stderr'):
- s = getattr(etype, '__name__', repr(etype))
- print >> sys.stderr, "debug: exception-type: ", s
- print >> sys.stderr, "debug: exception-value:", str(evalue)
- tbentry = etraceback
- if tbentry:
- while tbentry.tb_next:
- tbentry = tbentry.tb_next
- lineno = tbentry.tb_lineno
- filename = tbentry.tb_frame.f_code.co_filename
- print >> sys.stderr, "debug: exception-tb: %s:%d" % (
- filename, lineno)
+ stderr = sys.stderr
+ except AttributeError:
+ pass # too bad
+ else:
+ print >> stderr, 'Error calling sys.excepthook:'
+ originalexcepthook(*sys.exc_info())
+ print >> stderr
+ print >> stderr, 'Original exception was:'
- # set the sys.last_xxx attributes
- sys.last_type = etype
- sys.last_value = evalue
- sys.last_traceback = etraceback
+ # we only get here if sys.excepthook didn't do its job
+ originalexcepthook(etype, evalue, etraceback)
- # call sys.excepthook
- hook = getattr(sys, 'excepthook', originalexcepthook)
- hook(etype, evalue, etraceback)
- return False # done
-
- except:
- try:
- stderr = sys.stderr
- except AttributeError:
- pass # too bad
- else:
- print >> stderr, 'Error calling sys.excepthook:'
- originalexcepthook(*sys.exc_info())
- print >> stderr
- print >> stderr, 'Original exception was:'
-
- # we only get here if sys.excepthook didn't do its job
- originalexcepthook(etype, evalue, etraceback)
- return False
-
- return True # success
# ____________________________________________________________
# Option parsing
-def print_info():
+def print_info(*args):
try:
options = sys.pypy_translation_info
except AttributeError:
@@ -120,15 +125,17 @@ def print_info():
optitems.sort()
for name, value in optitems:
print ' %51s: %s' % (name, value)
+ raise SystemExit
-def print_help():
+def print_help(*args):
print 'usage: %s [options]' % (sys.executable,)
print __doc__.rstrip()
if 'pypyjit' in sys.builtin_module_names:
- print_jit_help()
+ _print_jit_help()
print
+ raise SystemExit
-def print_jit_help():
+def _print_jit_help():
import pypyjit
items = pypyjit.defaults.items()
items.sort()
@@ -136,6 +143,18 @@ def print_jit_help():
print ' --jit %s=N %slow-level JIT parameter (default %s)' % (
key, ' '*(18-len(key)), value)
+def print_version(*args):
+ print "Python", sys.version
+ raise SystemExit
+
+def set_jit_option(options, jitparam, *args):
+ if 'pypyjit' not in sys.builtin_module_names:
+ print >> sys.stderr, ("Warning: No jit support in %s" %
+ (sys.executable,))
+ else:
+ import pypyjit
+ pypyjit.set_param(jitparam)
+
class CommandLineError(Exception):
pass
@@ -171,18 +190,6 @@ if 'nt' in sys.builtin_module_names:
else:
IS_WINDOWS = False
-def get_argument(option, argv, i):
- arg = argv[i]
- n = len(option)
- if len(arg) > n:
- return arg[n:], i
- else:
- i += 1
- if i >= len(argv):
- raise CommandLineError('Argument expected for the %s option' %
- option)
- return argv[i], i
-
def get_library_path(executable):
search = executable
while 1:
@@ -200,7 +207,7 @@ def get_library_path(executable):
break # found!
return newpath
-def setup_initial_paths(executable, nanos, readenv=True, **extra):
+def setup_initial_paths(executable, nanos, ignore_environment=False, **extra):
# a substituted os if we are translated
global os
os = nanos
@@ -221,6 +228,7 @@ def setup_initial_paths(executable, nano
sys.executable = os.path.abspath(executable)
newpath = get_library_path(executable)
+ readenv = not ignore_environment
path = readenv and os.getenv('PYTHONPATH')
if path:
newpath = path.split(os.pathsep) + newpath
@@ -232,84 +240,181 @@ def setup_initial_paths(executable, nano
sys.path.append(dir)
_seen[dir] = True
+# Order is significant!
+sys_flags = (
+ "debug",
+ "py3k_warning",
+ "division_warning",
+ "division_new",
+ "inspect",
+ "interactive",
+ "optimize",
+ "dont_write_bytecode",
+ "no_user_site",
+ "no_site",
+ "ignore_environment",
+ "tabcheck",
+ "verbose",
+ "unicode",
+ "bytes_warning",
+)
+
+
+default_options = dict.fromkeys(
+ sys_flags +
+ ("run_command",
+ "run_module",
+ "run_stdin",
+ "warnoptions",
+ "unbuffered"), 0)
+
+
+PYTHON26 = False
+
+def simple_option(options, name, iterargv):
+ options[name] += 1
+
+def div_option(options, div, iterargv):
+ if div == "warn":
+ options["division_warning"] = 1
+ elif div == "warnall":
+ options["division_warning"] = 2
+ elif div == "new":
+ options["division_new"] = 1
+ elif div != "old":
+ raise CommandLineError("invalid division option: %r" % (div,))
+
+def c_option(options, runcmd, iterargv):
+ options["run_command"] = runcmd
+ return ['-c'] + list(iterargv)
+
+def m_option(options, runmodule, iterargv):
+ options["run_module"] = True
+ return [runmodule] + list(iterargv)
+
+def W_option(options, warnoption, iterargv):
+ options["warnoptions"].append(warnoption)
+
+def end_options(options, _, iterargv):
+ return list(iterargv)
+
+cmdline_options = {
+ # simple options just increment the counter of the options listed above
+ 'd': (simple_option, 'debug'),
+ 'i': (simple_option, 'interactive'),
+ 'O': (simple_option, 'optimize'),
+ 'S': (simple_option, 'no_site'),
+ 'E': (simple_option, 'ignore_environment'),
+ 't': (simple_option, 'tabcheck'),
+ 'v': (simple_option, 'verbose'),
+ 'U': (simple_option, 'unicode'),
+ 'u': (simple_option, 'unbuffered'),
+ # more complex options
+ 'Q': (div_option, Ellipsis),
+ 'c': (c_option, Ellipsis),
+ 'm': (m_option, Ellipsis),
+ 'W': (W_option, Ellipsis),
+ 'V': (print_version, None),
+ '--version': (print_version, None),
+ '--info': (print_info, None),
+ 'h': (print_help, None),
+ '--help': (print_help, None),
+ '--jit': (set_jit_option, Ellipsis),
+ '--': (end_options, None),
+ }
+
+if PYTHON26:
+ cmdline_options.update({
+ '3': (simple_option, 'py3k_warning'),
+ 'B': (simple_option, 'dont_write_bytecode'),
+ 's': (simple_option, 'no_user_site'),
+ 'b': (simple_option, 'bytes_warning'),
+ })
+
+
+def handle_argument(c, options, iterargv, iterarg=iter(())):
+ function, funcarg = cmdline_options[c]
+ #
+ # If needed, fill in the real argument by taking it from the command line
+ if funcarg is Ellipsis:
+ remaining = list(iterarg)
+ if remaining:
+ funcarg = ''.join(remaining)
+ else:
+ try:
+ funcarg = iterargv.next()
+ except StopIteration:
+ if len(c) == 1:
+ c = '-' + c
+ raise CommandLineError('Argument expected for the %r option' % c)
+ #
+ return function(options, funcarg, iterargv)
+
def parse_command_line(argv):
- go_interactive = False
- run_command = False
- import_site = True
- i = 0
- run_module = False
- run_stdin = False
- warnoptions = []
- unbuffered = False
- readenv = True
- while i < len(argv):
- arg = argv[i]
- if not arg.startswith('-'):
- break
- if arg == '-i':
- go_interactive = True
- elif arg.startswith('-c'):
- cmd, i = get_argument('-c', argv, i)
- argv[i] = '-c'
- run_command = True
- break
- elif arg == '-E':
- readenv = False
- elif arg == '-u':
- unbuffered = True
- elif arg == '-O' or arg == '-OO':
- pass
- elif arg == '--version' or arg == '-V':
- print "Python", sys.version
- return
- elif arg == '--info':
- print_info()
- return
- elif arg == '-h' or arg == '--help':
- print_help()
- return
- elif arg == '-S':
- import_site = False
- elif arg == '-':
- run_stdin = True
- break # not an option but a file name representing stdin
- elif arg.startswith('-m'):
- module, i = get_argument('-m', argv, i)
- argv[i] = module
- run_module = True
- break
- elif arg.startswith('-W'):
- warnoptions, i = get_argument('-W', argv, i)
- elif arg.startswith('--jit'):
- jitparam, i = get_argument('--jit', argv, i)
- if 'pypyjit' not in sys.builtin_module_names:
- print >> sys.stderr, ("Warning: No jit support in %s" %
- (sys.executable,))
- else:
- import pypyjit
- pypyjit.set_param(jitparam)
- elif arg == '--':
- i += 1
- break # terminates option list
+ options = default_options.copy()
+ options['warnoptions'] = []
+ #
+ iterargv = iter(argv)
+ argv = None
+ for arg in iterargv:
+ #
+ # If the next argument isn't at least two characters long or
+ # doesn't start with '-', stop processing
+ if len(arg) < 2 or arg[0] != '-':
+ if IS_WINDOWS and arg == '/?': # special case
+ print_help()
+ argv = [arg] + list(iterargv) # finishes processing
+ #
+ # If the next argument is directly in cmdline_options, handle
+ # it as a single argument
+ elif arg in cmdline_options:
+ argv = handle_argument(arg, options, iterargv)
+ #
+ # Else interpret the rest of the argument character by character
else:
- raise CommandLineError('unrecognized option %r' % (arg,))
- i += 1
- sys.argv[:] = argv[i:] # don't change the list that sys.argv is bound to
- if not sys.argv: # (relevant in case of "reload(sys)")
- sys.argv.append('')
- run_stdin = True
- return locals()
+ iterarg = iter(arg)
+ iterarg.next() # skip the '-'
+ for c in iterarg:
+ if c not in cmdline_options:
+ raise CommandLineError('Unknown option: -%s' % (c,))
+ argv = handle_argument(c, options, iterargv, iterarg)
-def run_command_line(go_interactive,
+ if not argv:
+ argv = ['']
+ options["run_stdin"] = True
+ elif argv[0] == '-':
+ options["run_stdin"] = True
+
+ # don't change the list that sys.argv is bound to
+ # (relevant in case of "reload(sys)")
+ sys.argv[:] = argv
+
+ if (options["interactive"] or
+ (not options["ignore_environment"] and os.getenv('PYTHONINSPECT'))):
+ options["inspect"] = True
+
+ if PYTHON26 and we_are_translated():
+ flags = [options[flag] for flag in sys_flags]
+ sys.flags = type(sys.flags)(flags)
+ sys.py3kwarning = sys.flags.py3k_warning
+
+## if not we_are_translated():
+## for key in sorted(options):
+## print '%40s: %s' % (key, options[key])
+## print '%40s: %s' % ("sys.argv", sys.argv)
+
+ return options
+
+def run_command_line(interactive,
+ inspect,
run_command,
- import_site,
+ no_site,
run_module,
run_stdin,
warnoptions,
unbuffered,
- readenv,
- cmd=None,
+ ignore_environment,
**ignored):
# with PyPy in top of CPython we can only have around 100
# but we need more in the translated PyPy for the compiler package
@@ -325,14 +430,18 @@ def run_command_line(go_interactive,
mainmodule = type(sys)('__main__')
sys.modules['__main__'] = mainmodule
- if import_site:
+ if not no_site:
try:
import site
except:
print >> sys.stderr, "'import site' failed"
+ readenv = not ignore_environment
+ pythonwarnings = readenv and os.getenv('PYTHONWARNINGS')
+ if pythonwarnings:
+ warnoptions.extend(pythonwarnings.split(','))
if warnoptions:
- sys.warnoptions.append(warnoptions)
+ sys.warnoptions[:] = warnoptions
from warnings import _processoptions
_processoptions(sys.warnoptions)
@@ -352,26 +461,28 @@ def run_command_line(go_interactive,
signal.signal(signal.SIGXFSZ, signal.SIG_IGN)
def inspect_requested():
- # We get an interactive prompt in one of the following two cases:
+ # We get an interactive prompt in one of the following three cases:
#
- # * go_interactive=True, either from the "-i" option or
- # from the fact that we printed the banner;
+ # * interactive=True, from the "-i" option
+ # or
+ # * inspect=True and stdin is a tty
# or
# * PYTHONINSPECT is set and stdin is a tty.
#
- return (go_interactive or
- (readenv and os.getenv('PYTHONINSPECT') and sys.stdin.isatty()))
+ return (interactive or
+ ((inspect or (readenv and os.getenv('PYTHONINSPECT')))
+ and sys.stdin.isatty()))
success = True
try:
- if run_command:
+ if run_command != 0:
# handle the "-c" command
# Put '' on sys.path
sys.path.insert(0, '')
def run_it():
- exec cmd in mainmodule.__dict__
+ exec run_command in mainmodule.__dict__
success = run_toplevel(run_it)
elif run_module:
# handle the "-m" command
@@ -389,7 +500,7 @@ def run_command_line(go_interactive,
# put it's directory on sys.path
sys.path.insert(0, '')
- if go_interactive or sys.stdin.isatty():
+ if interactive or sys.stdin.isatty():
# If stdin is a tty or if "-i" is specified, we print
# a banner and run $PYTHONSTARTUP.
print_banner()
@@ -410,7 +521,7 @@ def run_command_line(go_interactive,
exec co_python_startup in mainmodule.__dict__
run_toplevel(run_it)
# Then we need a prompt.
- go_interactive = True
+ inspect = True
else:
# If not interactive, just read and execute stdin normally.
def run_it():
@@ -426,14 +537,25 @@ def run_command_line(go_interactive,
sys.path.insert(0, scriptdir)
success = run_toplevel(execfile, sys.argv[0], mainmodule.__dict__)
- # start a prompt if requested
+ except SystemExit, e:
+ status = e.code
if inspect_requested():
+ display_exception()
+ else:
+ status = not success
+
+ # start a prompt if requested
+ if inspect_requested():
+ inteactive = False
+ try:
from _pypy_interact import interactive_console
success = run_toplevel(interactive_console, mainmodule)
- except SystemExit, e:
- return e.code
- else:
- return not success
+ except SystemExit, e:
+ status = e.code
+ else:
+ status = not success
+
+ return status
def resolvedirof(filename):
try:
@@ -461,8 +583,6 @@ def entry_point(executable, argv, nanos)
except CommandLineError, e:
print_error(str(e))
return 2
- if cmdline is None:
- return 0
setup_initial_paths(executable, nanos, **cmdline)
return run_command_line(**cmdline)
--- a/pypy/translator/goal/test2/test_app_main.py
+++ b/pypy/translator/goal/test2/test_app_main.py
@@ -43,6 +43,93 @@ crashing_demo_script = getscript("""
""")
+class TestParseCommandLine:
+
+ def check_options(self, options, sys_argv, **expected):
+ assert sys.argv == sys_argv
+ for key, value in expected.items():
+ assert options[key] == value
+ for key, value in options.items():
+ if key not in expected:
+ assert not value, (
+ "option %r has unexpectedly the value %r" % (key, value))
+
+ def check(self, argv, **expected):
+ import StringIO
+ from pypy.translator.goal import app_main
+ saved_sys_argv = sys.argv[:]
+ saved_sys_stdout = sys.stdout
+ saved_sys_stderr = sys.stdout
+ app_main.os = os
+ try:
+ sys.stdout = sys.stderr = StringIO.StringIO()
+ try:
+ options = app_main.parse_command_line(argv)
+ except SystemExit:
+ output = expected['output_contains']
+ assert output in sys.stdout.getvalue()
+ else:
+ self.check_options(options, **expected)
+ finally:
+ sys.argv[:] = saved_sys_argv
+ sys.stdout = saved_sys_stdout
+ sys.stderr = saved_sys_stderr
+
+ def test_all_combinations_I_can_think_of(self):
+ self.check([], sys_argv=[''], run_stdin=True)
+ self.check(['-'], sys_argv=['-'], run_stdin=True)
+ self.check(['-S'], sys_argv=[''], run_stdin=True, no_site=1)
+ self.check(['-OO'], sys_argv=[''], run_stdin=True, optimize=2)
+ self.check(['-O', '-O'], sys_argv=[''], run_stdin=True, optimize=2)
+ self.check(['-Qnew'], sys_argv=[''], run_stdin=True, division_new=1)
+ self.check(['-Qold'], sys_argv=[''], run_stdin=True, division_new=0)
+ self.check(['-Qwarn'], sys_argv=[''], run_stdin=True, division_warning=1)
+ self.check(['-Qwarnall'], sys_argv=[''], run_stdin=True,
+ division_warning=2)
+ self.check(['-Q', 'new'], sys_argv=[''], run_stdin=True, division_new=1)
+ self.check(['-SOQnew'], sys_argv=[''], run_stdin=True,
+ no_site=1, optimize=1, division_new=1)
+ self.check(['-SOQ', 'new'], sys_argv=[''], run_stdin=True,
+ no_site=1, optimize=1, division_new=1)
+ self.check(['-i'], sys_argv=[''], run_stdin=True,
+ interactive=1, inspect=1)
+ self.check(['-h'], output_contains='usage:')
+ self.check(['-S', '-tO', '-h'], output_contains='usage:')
+ self.check(['-S', '-thO'], output_contains='usage:')
+ self.check(['-S', '-tO', '--help'], output_contains='usage:')
+ self.check(['-S', '-tO', '--info'], output_contains='translation')
+ self.check(['-S', '-tO', '--version'], output_contains='Python')
+ self.check(['-S', '-tOV'], output_contains='Python')
+ self.check(['--jit', 'foobar', '-S'], sys_argv=[''],
+ run_stdin=True, no_site=1)
+ self.check(['-c', 'pass'], sys_argv=['-c'], run_command='pass')
+ self.check(['-cpass'], sys_argv=['-c'], run_command='pass')
+ self.check(['-cpass','x'], sys_argv=['-c','x'], run_command='pass')
+ self.check(['-Sc', 'pass'], sys_argv=['-c'], run_command='pass',
+ no_site=1)
+ self.check(['-Scpass'], sys_argv=['-c'], run_command='pass', no_site=1)
+ self.check(['-c', '', ''], sys_argv=['-c', ''], run_command='')
+ self.check(['-mfoo', 'bar', 'baz'], sys_argv=['foo', 'bar', 'baz'],
+ run_module=True)
+ self.check(['-m', 'foo', 'bar', 'baz'], sys_argv=['foo', 'bar', 'baz'],
+ run_module=True)
+ self.check(['-Smfoo', 'bar', 'baz'], sys_argv=['foo', 'bar', 'baz'],
+ run_module=True, no_site=1)
+ self.check(['-Sm', 'foo', 'bar', 'baz'], sys_argv=['foo', 'bar', 'baz'],
+ run_module=True, no_site=1)
+ self.check(['-', 'foo', 'bar'], sys_argv=['-', 'foo', 'bar'],
+ run_stdin=True)
+ self.check(['foo', 'bar'], sys_argv=['foo', 'bar'])
+ self.check(['foo', '-i'], sys_argv=['foo', '-i'])
+ self.check(['-i', 'foo'], sys_argv=['foo'], interactive=1, inspect=1)
+ self.check(['--', 'foo'], sys_argv=['foo'])
+ self.check(['--', '-i', 'foo'], sys_argv=['-i', 'foo'])
+ self.check(['--', '-', 'foo'], sys_argv=['-', 'foo'], run_stdin=True)
+ self.check(['-Wbog'], sys_argv=[''], warnoptions=['bog'], run_stdin=True)
+ self.check(['-W', 'ab', '-SWc'], sys_argv=[''], warnoptions=['ab', 'c'],
+ run_stdin=True, no_site=1)
+
+
class TestInteraction:
"""
These tests require pexpect (UNIX-only).
@@ -416,7 +503,7 @@ class TestNonInteractive:
def test_option_W_crashing(self):
data = self.run('-W')
- assert 'Argument expected for the -W option' in data
+ assert "Argument expected for the '-W' option" in data
def test_option_W_arg_ignored(self):
data = self.run('-Wc')
More information about the Pypy-commit
mailing list