[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