[Python-checkins] r59039 - in python/trunk: Include/import.h Lib/test/test_cmd_line.py Lib/test/test_cmd_line_script.py Misc/NEWS Modules/main.c Python/import.c

nick.coghlan python-checkins at python.org
Sun Nov 18 12:56:28 CET 2007


Author: nick.coghlan
Date: Sun Nov 18 12:56:28 2007
New Revision: 59039
Added:
 python/trunk/Lib/test/test_cmd_line_script.py (contents, props changed)
Modified:
 python/trunk/Include/import.h
 python/trunk/Lib/test/test_cmd_line.py
 python/trunk/Misc/NEWS
 python/trunk/Modules/main.c
 python/trunk/Python/import.c
Log:
Patch #1739468: Directories and zipfiles containing __main__.py are now executable
Modified: python/trunk/Include/import.h
==============================================================================
--- python/trunk/Include/import.h	(original)
+++ python/trunk/Include/import.h	Sun Nov 18 12:56:28 2007
@@ -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));
Modified: python/trunk/Lib/test/test_cmd_line.py
==============================================================================
--- python/trunk/Lib/test/test_cmd_line.py	(original)
+++ python/trunk/Lib/test/test_cmd_line.py	Sun Nov 18 12:56:28 2007
@@ -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
Added: python/trunk/Lib/test/test_cmd_line_script.py
==============================================================================
--- (empty file)
+++ python/trunk/Lib/test/test_cmd_line_script.py	Sun Nov 18 12:56:28 2007
@@ -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
+
+ at contextlib.contextmanager
+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()
Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Sun Nov 18 12:56:28 2007
@@ -12,6 +12,10 @@
 Core and builtins
 -----------------
 
+- Patch #1739468: Directories and zipfiles containing a __main__.py file can
+ now be directly executed by passing their name to the interpreter. The
+ directory/zipfile is automatically inserted as the first entry in sys.path.
+
 - Issue #1265: Fix a problem with sys.settrace, if the tracing function uses a
 generator expression when at the same time the executed code is closing a
 paused generator.
Modified: python/trunk/Modules/main.c
==============================================================================
--- python/trunk/Modules/main.c	(original)
+++ python/trunk/Modules/main.c	Sun Nov 18 12:56:28 2007
@@ -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
Modified: python/trunk/Python/import.c
==============================================================================
--- python/trunk/Python/import.c	(original)
+++ python/trunk/Python/import.c	Sun Nov 18 12:56:28 2007
@@ -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:
 	;
 }


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /