Kaydet (Commit) 327a39b0 authored tarafından Nick Coghlan's avatar Nick Coghlan

Patch #1739468: Directories and zipfiles containing __main__.py are now executable

üst 5cf449cf
...@@ -24,6 +24,7 @@ PyAPI_FUNC(PyObject *) PyImport_ImportModuleEx( ...@@ -24,6 +24,7 @@ PyAPI_FUNC(PyObject *) PyImport_ImportModuleEx(
#define PyImport_ImportModuleEx(n, g, l, f) \ #define PyImport_ImportModuleEx(n, g, l, f) \
PyImport_ImportModuleLevel(n, g, l, f, -1) 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_Import(PyObject *name);
PyAPI_FUNC(PyObject *) PyImport_ReloadModule(PyObject *m); PyAPI_FUNC(PyObject *) PyImport_ReloadModule(PyObject *m);
PyAPI_FUNC(void) PyImport_Cleanup(void); PyAPI_FUNC(void) PyImport_Cleanup(void);
...@@ -42,6 +43,7 @@ struct _inittab { ...@@ -42,6 +43,7 @@ struct _inittab {
void (*initfunc)(void); void (*initfunc)(void);
}; };
PyAPI_DATA(PyTypeObject) PyNullImporter_Type;
PyAPI_DATA(struct _inittab *) PyImport_Inittab; PyAPI_DATA(struct _inittab *) PyImport_Inittab;
PyAPI_FUNC(int) PyImport_AppendInittab(char *name, void (*initfunc)(void)); PyAPI_FUNC(int) PyImport_AppendInittab(char *name, void (*initfunc)(void));
......
# 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 test.test_support, unittest
import sys import sys
......
# 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
@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()
...@@ -12,6 +12,10 @@ What's New in Python 2.6 alpha 1? ...@@ -12,6 +12,10 @@ What's New in Python 2.6 alpha 1?
Core and builtins 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 - 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 generator expression when at the same time the executed code is closing a
paused generator. paused generator.
......
...@@ -141,7 +141,7 @@ static void RunStartupFile(PyCompilerFlags *cf) ...@@ -141,7 +141,7 @@ static void RunStartupFile(PyCompilerFlags *cf)
} }
static int RunModule(char *module) static int RunModule(char *module, int set_argv0)
{ {
PyObject *runpy, *runmodule, *runargs, *result; PyObject *runpy, *runmodule, *runargs, *result;
runpy = PyImport_ImportModule("runpy"); runpy = PyImport_ImportModule("runpy");
...@@ -155,7 +155,7 @@ static int RunModule(char *module) ...@@ -155,7 +155,7 @@ static int RunModule(char *module)
Py_DECREF(runpy); Py_DECREF(runpy);
return -1; return -1;
} }
runargs = Py_BuildValue("(s)", module); runargs = Py_BuildValue("(si)", module, set_argv0);
if (runargs == NULL) { if (runargs == NULL) {
fprintf(stderr, fprintf(stderr,
"Could not create arguments for runpy._run_module_as_main\n"); "Could not create arguments for runpy._run_module_as_main\n");
...@@ -177,6 +177,35 @@ static int RunModule(char *module) ...@@ -177,6 +177,35 @@ static int RunModule(char *module)
return 0; 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 /* Wait until threading._shutdown completes, provided
the threading module was imported in the first place. the threading module was imported in the first place.
The shutdown routine will wait until all non-daemon The shutdown routine will wait until all non-daemon
...@@ -388,39 +417,6 @@ Py_Main(int argc, char **argv) ...@@ -388,39 +417,6 @@ Py_Main(int argc, char **argv)
#else #else
filename = argv[_PyOS_optind]; filename = argv[_PyOS_optind];
#endif #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); stdin_is_interactive = Py_FdIsInteractive(stdin, (char *)0);
...@@ -515,21 +511,65 @@ Py_Main(int argc, char **argv) ...@@ -515,21 +511,65 @@ Py_Main(int argc, char **argv)
sts = PyRun_SimpleStringFlags(command, &cf) != 0; sts = PyRun_SimpleStringFlags(command, &cf) != 0;
free(command); free(command);
} else if (module) { } else if (module) {
sts = RunModule(module); sts = RunModule(module, 1);
free(module); free(module);
} }
else { else {
if (filename == NULL && stdin_is_interactive) { if (filename == NULL && stdin_is_interactive) {
Py_InspectFlag = 0; /* do exit on SystemExit */ Py_InspectFlag = 0; /* do exit on SystemExit */
RunStartupFile(&cf); RunStartupFile(&cf);
} }
/* XXX */ /* XXX */
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( sts = PyRun_AnyFileExFlags(
fp, fp,
filename == NULL ? "<stdin>" : filename, filename == NULL ? "<stdin>" : filename,
filename != NULL, &cf) != 0; filename != NULL, &cf) != 0;
} }
}
/* Check this environment variable at the end, to give programs the /* Check this environment variable at the end, to give programs the
* opportunity to set it from Python. * opportunity to set it from Python.
*/ */
......
...@@ -104,7 +104,6 @@ static const struct filedescr _PyImport_StandardFiletab[] = { ...@@ -104,7 +104,6 @@ static const struct filedescr _PyImport_StandardFiletab[] = {
}; };
#endif #endif
static PyTypeObject NullImporterType; /* Forward reference */
/* Initialize things */ /* Initialize things */
...@@ -167,7 +166,7 @@ _PyImportHooks_Init(void) ...@@ -167,7 +166,7 @@ _PyImportHooks_Init(void)
/* adding sys.path_hooks and sys.path_importer_cache, setting up /* adding sys.path_hooks and sys.path_importer_cache, setting up
zipimport */ zipimport */
if (PyType_Ready(&NullImporterType) < 0) if (PyType_Ready(&PyNullImporter_Type) < 0)
goto error; goto error;
if (Py_VerboseFlag) if (Py_VerboseFlag)
...@@ -1088,7 +1087,7 @@ get_path_importer(PyObject *path_importer_cache, PyObject *path_hooks, ...@@ -1088,7 +1087,7 @@ get_path_importer(PyObject *path_importer_cache, PyObject *path_hooks,
} }
if (importer == NULL) { if (importer == NULL) {
importer = PyObject_CallFunctionObjArgs( importer = PyObject_CallFunctionObjArgs(
(PyObject *)&NullImporterType, p, NULL (PyObject *)&PyNullImporter_Type, p, NULL
); );
if (importer == NULL) { if (importer == NULL) {
if (PyErr_ExceptionMatches(PyExc_ImportError)) { if (PyErr_ExceptionMatches(PyExc_ImportError)) {
...@@ -1106,6 +1105,20 @@ get_path_importer(PyObject *path_importer_cache, PyObject *path_hooks, ...@@ -1106,6 +1105,20 @@ get_path_importer(PyObject *path_importer_cache, PyObject *path_hooks,
return importer; 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 /* Search the path (default sys.path) for a module. Return the
corresponding filedescr struct, and (via return arguments) the corresponding filedescr struct, and (via return arguments) the
pathname and an open file. Return NULL if the module is not found. */ pathname and an open file. Return NULL if the module is not found. */
...@@ -3049,7 +3062,7 @@ static PyMethodDef NullImporter_methods[] = { ...@@ -3049,7 +3062,7 @@ static PyMethodDef NullImporter_methods[] = {
}; };
static PyTypeObject NullImporterType = { PyTypeObject PyNullImporter_Type = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"imp.NullImporter", /*tp_name*/ "imp.NullImporter", /*tp_name*/
sizeof(NullImporter), /*tp_basicsize*/ sizeof(NullImporter), /*tp_basicsize*/
...@@ -3096,7 +3109,7 @@ initimp(void) ...@@ -3096,7 +3109,7 @@ initimp(void)
{ {
PyObject *m, *d; PyObject *m, *d;
if (PyType_Ready(&NullImporterType) < 0) if (PyType_Ready(&PyNullImporter_Type) < 0)
goto failure; goto failure;
m = Py_InitModule4("imp", imp_methods, doc_imp, m = Py_InitModule4("imp", imp_methods, doc_imp,
...@@ -3118,8 +3131,8 @@ initimp(void) ...@@ -3118,8 +3131,8 @@ initimp(void)
if (setint(d, "PY_CODERESOURCE", PY_CODERESOURCE) < 0) goto failure; if (setint(d, "PY_CODERESOURCE", PY_CODERESOURCE) < 0) goto failure;
if (setint(d, "IMP_HOOK", IMP_HOOK) < 0) goto failure; if (setint(d, "IMP_HOOK", IMP_HOOK) < 0) goto failure;
Py_INCREF(&NullImporterType); Py_INCREF(&PyNullImporter_Type);
PyModule_AddObject(m, "NullImporter", (PyObject *)&NullImporterType); PyModule_AddObject(m, "NullImporter", (PyObject *)&PyNullImporter_Type);
failure: failure:
; ;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment