Nick Coghlan added the comment:

I worked out what was wrong with my unit tests (I was incorrectly
including the path information when adding the test script to the zipfile)

I've updated the patch here, and will be committing the change once the
test suite finishes running.

----------
versions: +Python 2.6
Added file: http://bugs.python.org/file8770/runmain_with_tests.diff

_____________________________________
Tracker <[EMAIL PROTECTED]>
<http://bugs.python.org/issue1739468>
_____________________________________
Index: Python/import.c
===================================================================
--- Python/import.c     (revision 59036)
+++ Python/import.c     (working copy)
@@ -104,7 +104,6 @@
 };
 #endif
 
-static PyTypeObject NullImporterType;  /* Forward reference */
 
 /* Initialize things */
 
@@ -167,7 +166,7 @@
 
        /* adding sys.path_hooks and sys.path_importer_cache, setting up
           zipimport */
-       if (PyType_Ready(&NullImporterType) < 0)
+       if (PyType_Ready(&PyNullImporter_Type) < 0)
                goto error;
 
        if (Py_VerboseFlag)
@@ -1088,7 +1087,7 @@
        }
        if (importer == NULL) {
                importer = PyObject_CallFunctionObjArgs(
-                       (PyObject *)&NullImporterType, p, NULL
+                       (PyObject *)&PyNullImporter_Type, p, NULL
                );
                if (importer == NULL) {
                        if (PyErr_ExceptionMatches(PyExc_ImportError)) {
@@ -1106,6 +1105,20 @@
        return importer;
 }
 
+PyAPI_FUNC(PyObject *)
+PyImport_GetImporter(PyObject *path) {
+        PyObject *importer=NULL, *path_importer_cache=NULL, *path_hooks=NULL;
+
+       if ((path_importer_cache = PySys_GetObject("path_importer_cache"))) {
+               if ((path_hooks = PySys_GetObject("path_hooks"))) {
+                       importer = get_path_importer(path_importer_cache,
+                                                    path_hooks, path);
+               }
+       }
+       Py_XINCREF(importer); /* get_path_importer returns a borrowed reference 
*/
+       return importer;
+}
+
 /* Search the path (default sys.path) for a module.  Return the
    corresponding filedescr struct, and (via return arguments) the
    pathname and an open file.  Return NULL if the module is not found. */
@@ -3049,7 +3062,7 @@
 };
 
 
-static PyTypeObject NullImporterType = {
+PyTypeObject PyNullImporter_Type = {
        PyVarObject_HEAD_INIT(NULL, 0)
        "imp.NullImporter",        /*tp_name*/
        sizeof(NullImporter),      /*tp_basicsize*/
@@ -3096,7 +3109,7 @@
 {
        PyObject *m, *d;
 
-       if (PyType_Ready(&NullImporterType) < 0)
+       if (PyType_Ready(&PyNullImporter_Type) < 0)
                goto failure;
 
        m = Py_InitModule4("imp", imp_methods, doc_imp,
@@ -3118,8 +3131,8 @@
        if (setint(d, "PY_CODERESOURCE", PY_CODERESOURCE) < 0) goto failure;
        if (setint(d, "IMP_HOOK", IMP_HOOK) < 0) goto failure;
 
-       Py_INCREF(&NullImporterType);
-       PyModule_AddObject(m, "NullImporter", (PyObject *)&NullImporterType);
+       Py_INCREF(&PyNullImporter_Type);
+       PyModule_AddObject(m, "NullImporter", (PyObject *)&PyNullImporter_Type);
   failure:
        ;
 }
Index: Include/import.h
===================================================================
--- Include/import.h    (revision 59036)
+++ Include/import.h    (working copy)
@@ -24,6 +24,7 @@
 #define PyImport_ImportModuleEx(n, g, l, f) \
        PyImport_ImportModuleLevel(n, g, l, f, -1)
 
+PyAPI_FUNC(PyObject *) PyImport_GetImporter(PyObject *path);
 PyAPI_FUNC(PyObject *) PyImport_Import(PyObject *name);
 PyAPI_FUNC(PyObject *) PyImport_ReloadModule(PyObject *m);
 PyAPI_FUNC(void) PyImport_Cleanup(void);
@@ -42,6 +43,7 @@
     void (*initfunc)(void);
 };
 
+PyAPI_DATA(PyTypeObject) PyNullImporter_Type;
 PyAPI_DATA(struct _inittab *) PyImport_Inittab;
 
 PyAPI_FUNC(int) PyImport_AppendInittab(char *name, void (*initfunc)(void));
Index: Lib/test/test_cmd_line.py
===================================================================
--- Lib/test/test_cmd_line.py   (revision 59036)
+++ Lib/test/test_cmd_line.py   (working copy)
@@ -1,3 +1,6 @@
+# Tests invocation of the interpreter with various command line arguments
+# All tests are executed with environment variables ignored
+# See test_cmd_line_script.py for testing of script execution
 
 import test.test_support, unittest
 import sys
Index: Lib/test/test_cmd_line_script.py
===================================================================
--- Lib/test/test_cmd_line_script.py    (revision 0)
+++ Lib/test/test_cmd_line_script.py    (revision 0)
@@ -0,0 +1,145 @@
+# Tests command line execution of scripts
+from __future__ import with_statement
+
+import unittest
+import os
+import os.path
+import sys
+import test
+import tempfile
+import subprocess
+import py_compile
+import contextlib
+import shutil
+import zipfile
+
+verbose = test.test_support.verbose
+
+# XXX ncoghlan: Should we consider moving these to test_support?
+from test_cmd_line import _spawn_python, _kill_python
+
+def _run_python(*args):
+    if __debug__:
+      p = _spawn_python(*args)
+    else:
+      p = _spawn_python('-O', *args)
+    stdout_data = _kill_python(p)
+    return p.wait(), stdout_data
+
[EMAIL PROTECTED]
+def temp_dir():
+    dirname = tempfile.mkdtemp()
+    try:
+        yield dirname
+    finally:
+        shutil.rmtree(dirname)
+
+test_source = ("""\
+# Script may be run with optimisation enabled, so don't rely on assert
+# statements being executed
+def assertEqual(lhs, rhs):
+    if lhs != rhs:
+        raise AssertionError("%r != %r" % (lhs, rhs))
+def assertIdentical(lhs, rhs):
+    if lhs is not rhs:
+        raise AssertionError("%r is not %r" % (lhs, rhs))
+# Check basic code execution
+result = ['Top level assignment']
+def f():
+    result.append('Lower level reference')
+f()
+assertEqual(result, ['Top level assignment', 'Lower level reference'])
+# Check population of magic variables
+assertEqual(__name__, '__main__')
+print '__file__==%r' % __file__
+# Check the sys module
+import sys
+assertIdentical(globals(), sys.modules[__name__].__dict__)
+print 'sys.argv[0]==%r' % sys.argv[0]
+""")
+
+def _make_test_script(script_dir, script_basename):
+    script_filename = script_basename+os.extsep+"py"
+    script_name = os.path.join(script_dir, script_filename)
+    script_file = open(script_name, "w")
+    script_file.write(test_source)
+    script_file.close()
+    return script_name
+
+def _compile_test_script(script_name):
+    py_compile.compile(script_name, doraise=True)
+    if __debug__:
+        compiled_name = script_name + 'c'
+    else:
+        compiled_name = script_name + 'o'
+    return compiled_name
+
+def _make_test_zip(zip_dir, zip_basename, script_name):
+    zip_filename = zip_basename+os.extsep+"zip"
+    zip_name = os.path.join(zip_dir, zip_filename)
+    zip_file = zipfile.ZipFile(zip_name, 'w')
+    zip_file.write(script_name, os.path.basename(script_name))
+    zip_file.close()
+    # if verbose:
+    #    zip_file = zipfile.ZipFile(zip_name, 'r')
+    #    print "Contents of %r:" % zip_name
+    #    zip_file.printdir()
+    #    zip_file.close()
+    return zip_name
+
+class CmdLineTest(unittest.TestCase):
+    def _check_script(self, script_name, expected_file, expected_argv0):
+        exit_code, data = _run_python(script_name)
+        # if verbose:
+        #    print "Output from test script %r:" % script_name
+        #    print data
+        self.assertEqual(exit_code, 0)
+        printed_file = '__file__==%r' % expected_file
+        printed_argv0 = 'sys.argv[0]==%r' % expected_argv0
+        self.assert_(printed_file in data)
+        self.assert_(printed_argv0 in data)
+
+    def test_basic_script(self):
+        with temp_dir() as script_dir:
+            script_name = _make_test_script(script_dir, "script")
+            self._check_script(script_name, script_name, script_name)
+
+    def test_script_compiled(self):
+        with temp_dir() as script_dir:
+            script_name = _make_test_script(script_dir, "script")
+            compiled_name = _compile_test_script(script_name)
+            os.remove(script_name)
+            self._check_script(compiled_name, compiled_name, compiled_name)
+
+    def test_directory(self):
+        with temp_dir() as script_dir:
+            script_name = _make_test_script(script_dir, "__main__")
+            self._check_script(script_dir, script_name, script_dir)
+
+    def test_directory_compiled(self):
+        with temp_dir() as script_dir:
+            script_name = _make_test_script(script_dir, "__main__")
+            compiled_name = _compile_test_script(script_name)
+            os.remove(script_name)
+            self._check_script(script_dir, compiled_name, script_dir)
+
+    def test_zipfile(self):
+        with temp_dir() as script_dir:
+            script_name = _make_test_script(script_dir, "__main__")
+            zip_name = _make_test_zip(script_dir, "test_zip", script_name)
+            self._check_script(zip_name, None, zip_name)
+
+    def test_zipfile_compiled(self):
+        with temp_dir() as script_dir:
+            script_name = _make_test_script(script_dir, "__main__")
+            compiled_name = _compile_test_script(script_name)
+            zip_name = _make_test_zip(script_dir, "test_zip", compiled_name)
+            self._check_script(zip_name, None, zip_name)
+
+
+def test_main():
+    test.test_support.run_unittest(CmdLineTest)
+    test.test_support.reap_children()
+
+if __name__ == "__main__":
+    test_main()

Property changes on: Lib/test/test_cmd_line_script.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Index: Modules/main.c
===================================================================
--- Modules/main.c      (revision 59036)
+++ Modules/main.c      (working copy)
@@ -141,7 +141,7 @@
 }
 
 
-static int RunModule(char *module)
+static int RunModule(char *module, int set_argv0)
 {
        PyObject *runpy, *runmodule, *runargs, *result;
        runpy = PyImport_ImportModule("runpy");
@@ -155,7 +155,7 @@
                Py_DECREF(runpy);
                return -1;
        }
-       runargs = Py_BuildValue("(s)", module);
+       runargs = Py_BuildValue("(si)", module, set_argv0);
        if (runargs == NULL) {
                fprintf(stderr,
                        "Could not create arguments for 
runpy._run_module_as_main\n");
@@ -177,6 +177,35 @@
        return 0;
 }
 
+static int RunMainFromImporter(char *filename)
+{
+       PyObject *argv0 = NULL, *importer = NULL;
+
+       if (
+               (argv0 = PyString_FromString(filename)) && 
+               (importer = PyImport_GetImporter(argv0)) &&
+               (importer->ob_type != &PyNullImporter_Type))
+       {
+                /* argv0 is usable as an import source, so
+                       put it in sys.path[0] and import __main__ */
+               PyObject *sys_path = NULL;
+               if (
+                       (sys_path = PySys_GetObject("path")) &&
+                       !PyList_SetItem(sys_path, 0, argv0)
+               ) {
+                       Py_INCREF(argv0);
+                       Py_CLEAR(importer);
+                       sys_path = NULL;
+                       return RunModule("__main__", 0) != 0;
+               }
+       }
+       PyErr_Clear();
+       Py_CLEAR(argv0);
+       Py_CLEAR(importer);
+       return -1;
+}
+
+
 /* Wait until threading._shutdown completes, provided
    the threading module was imported in the first place.
    The shutdown routine will wait until all non-daemon
@@ -388,39 +417,6 @@
 #else
                filename = argv[_PyOS_optind];
 #endif
-               if (filename != NULL) {
-                       if ((fp = fopen(filename, "r")) == NULL) {
-#ifdef HAVE_STRERROR
-                               fprintf(stderr, "%s: can't open file '%s': 
[Errno %d] %s\n",
-                                       argv[0], filename, errno, 
strerror(errno));
-#else
-                               fprintf(stderr, "%s: can't open file '%s': 
Errno %d\n",
-                                       argv[0], filename, errno);
-#endif
-                               return 2;
-                       }
-                       else if (skipfirstline) {
-                               int ch;
-                               /* Push back first newline so line numbers
-                                  remain the same */
-                               while ((ch = getc(fp)) != EOF) {
-                                       if (ch == '\n') {
-                                               (void)ungetc(ch, fp);
-                                               break;
-                                       }
-                               }
-                       }
-                       {
-                               /* XXX: does this work on Win/Win64? (see 
posix_fstat) */
-                               struct stat sb;
-                               if (fstat(fileno(fp), &sb) == 0 &&
-                                   S_ISDIR(sb.st_mode)) {
-                                       fprintf(stderr, "%s: '%s' is a 
directory, cannot continue\n", argv[0], filename);
-                                       fclose(fp);
-                                       return 1;
-                               }
-                       }
-               }
        }
 
        stdin_is_interactive = Py_FdIsInteractive(stdin, (char *)0);
@@ -515,19 +511,63 @@
                sts = PyRun_SimpleStringFlags(command, &cf) != 0;
                free(command);
        } else if (module) {
-               sts = RunModule(module);
+               sts = RunModule(module, 1);
                free(module);
        }
        else {
+
                if (filename == NULL && stdin_is_interactive) {
                        Py_InspectFlag = 0; /* do exit on SystemExit */
                        RunStartupFile(&cf);
                }
                /* XXX */
-               sts = PyRun_AnyFileExFlags(
-                       fp,
-                       filename == NULL ? "<stdin>" : filename,
-                       filename != NULL, &cf) != 0;
+
+               sts = -1;       /* keep track of whether we've already run 
__main__ */
+
+               if (filename != NULL) {
+                       sts = RunMainFromImporter(filename);
+               }
+
+               if (sts==-1 && filename!=NULL) {
+                       if ((fp = fopen(filename, "r")) == NULL) {
+#ifdef HAVE_STRERROR
+                               fprintf(stderr, "%s: can't open file '%s': 
[Errno %d] %s\n",
+                                       argv[0], filename, errno, 
strerror(errno));
+#else
+                               fprintf(stderr, "%s: can't open file '%s': 
Errno %d\n",
+                                       argv[0], filename, errno);
+#endif
+                               return 2;
+                       }
+                       else if (skipfirstline) {
+                               int ch;
+                               /* Push back first newline so line numbers
+                                  remain the same */
+                               while ((ch = getc(fp)) != EOF) {
+                                       if (ch == '\n') {
+                                               (void)ungetc(ch, fp);
+                                               break;
+                                       }
+                               }
+                       }
+                       {
+                               /* XXX: does this work on Win/Win64? (see 
posix_fstat) */
+                               struct stat sb;
+                               if (fstat(fileno(fp), &sb) == 0 &&
+                                   S_ISDIR(sb.st_mode)) {
+                                       fprintf(stderr, "%s: '%s' is a 
directory, cannot continue\n", argv[0], filename);
+                                       return 1;
+                               }
+                       }
+               }
+
+               if (sts==-1) {
+                       sts = PyRun_AnyFileExFlags(
+                               fp,
+                               filename == NULL ? "<stdin>" : filename,
+                               filename != NULL, &cf) != 0;
+               }
+               
        }
 
        /* Check this environment variable at the end, to give programs the
_______________________________________________
Python-bugs-list mailing list 
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to