mirror of
https://github.com/python/cpython.git
synced 2025-07-29 14:15:07 +00:00
Patch #1739468: Directories and zipfiles containing __main__.py are now executable
This commit is contained in:
parent
5cf449cfb2
commit
327a39b047
6 changed files with 254 additions and 47 deletions
|
@ -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 {
|
||||||
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));
|
||||||
|
|
|
@ -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 test.test_support, unittest
|
||||||
import sys
|
import sys
|
||||||
|
|
145
Lib/test/test_cmd_line_script.py
Normal file
145
Lib/test/test_cmd_line_script.py
Normal file
|
@ -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
|
||||||
|
|
||||||
|
@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?
|
||||||
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.
|
||||||
|
|
112
Modules/main.c
112
Modules/main.c
|
@ -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)
|
||||||
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)
|
||||||
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)
|
||||||
#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)
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static PyTypeObject NullImporterType; /* Forward reference */
|
|
||||||
|
|
||||||
/* Initialize things */
|
/* Initialize things */
|
||||||
|
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
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,
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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:
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue