Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3f48ae3

Browse files
committed
Merged revisions 69419-69420 via svnmerge from
svn+ssh://[email protected]/python/trunk ........ r69419 | nick.coghlan | 2009-02-08 11:26:34 +1000 (Sun, 08 Feb 2009) | 1 line Issue 4195: Restore the ability to execute packages with the -m switch (but this time in a way that leaves the import machinery in a valid state). (Original patch by Andi Vajda) ........ r69420 | nick.coghlan | 2009-02-08 11:46:01 +1000 (Sun, 08 Feb 2009) | 1 line Mention patch submitter in NEWS entry for r69419 ........
1 parent f72d9fb commit 3f48ae3

6 files changed

Lines changed: 112 additions & 13 deletions

File tree

Doc/library/runpy.rst

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,22 @@ The :mod:`runpy` module provides a single function:
2626
mechanism (refer to PEP 302 for details) and then executed in a fresh module
2727
namespace.
2828

29+
If the supplied module name refers to a package rather than a normal module,
30+
then that package is imported and the ``__main__`` submodule within that
31+
package is then executed and the resulting module globals dictionary returned.
32+
2933
The optional dictionary argument *init_globals* may be used to pre-populate the
3034
globals dictionary before the code is executed. The supplied dictionary will not
3135
be modified. If any of the special global variables below are defined in the
3236
supplied dictionary, those definitions are overridden by the ``run_module``
3337
function.
3438

35-
The special global variables ``__name__``, ``__file__``, ``__loader__`` and
36-
``__builtins__`` are set in the globals dictionary before the module code is
37-
executed.
39+
The special global variables ``__name__``, ``__file__``, ``__loader__``,
40+
``__builtins__`` and ``__package__`` are set in the globals dictionary before
41+
the module code is executed.
3842

39-
``__name__`` is set to *run_name* if this optional argument is supplied, and the
43+
``__name__`` is set to *run_name* if this optional argument is supplied, to
44+
``mod_name + '.__main__'`` if the named module is a package and to the
4045
*mod_name* argument otherwise.
4146

4247
``__loader__`` is set to the PEP 302 module loader used to retrieve the code for
@@ -48,6 +53,9 @@ The :mod:`runpy` module provides a single function:
4853
``__builtins__`` is automatically initialised with a reference to the top level
4954
namespace of the :mod:`builtins` module.
5055

56+
``__package__`` is set to *mod_name* if the named module is a package and to
57+
``mod_name.rpartition('.')[0]`` otherwise.
58+
5159
If the argument *alter_sys* is supplied and evaluates to ``True``, then
5260
``sys.argv[0]`` is updated with the value of ``__file__`` and
5361
``sys.modules[__name__]`` is updated with a temporary module object for the
@@ -60,8 +68,15 @@ The :mod:`runpy` module provides a single function:
6068
function from threaded code.
6169

6270

71+
.. versionchanged:: 3.1
72+
Added ability to execute packages by looking for a ``__main__`` submodule
73+
74+
6375
.. seealso::
6476

6577
:pep:`338` - Executing modules as scripts
66-
PEP written and implemented by Nick Coghlan.
78+
PEP written and implemented by Nick Coghlan.
79+
80+
:pep:`366` - Main module explicit relative imports
81+
PEP written and implemented by Nick Coghlan.
6782

Doc/using/cmdline.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ source.
7878
the implementation may not always enforce this (e.g. it may allow you to
7979
use a name that includes a hyphen).
8080

81+
Package names are also permitted. When a package name is supplied instead
82+
of a normal module, the interpreter will execute ``<pkg>.__main__`` as
83+
the main module. This behaviour is deliberately similar to the handling
84+
of directories and zipfiles that are passed to the interpreter as the
85+
script argument.
86+
8187
.. note::
8288

8389
This option cannot be used with builtin modules and extension modules
@@ -97,11 +103,14 @@ source.
97103

98104
.. seealso::
99105
:func:`runpy.run_module`
100-
The actual implementation of this feature.
106+
Equivalent functionality directly available to Python code
101107

102108
:pep:`338` -- Executing modules as scripts
103109

104110

111+
.. versionchanged:: 3.1
112+
Supply the package name to run a ``__main__`` submodule.
113+
105114
.. describe:: -
106115

107116
Read commands from standard input (:data:`sys.stdin`). If standard input is

Lib/runpy.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,19 @@ def _get_module_details(mod_name):
8080
if loader is None:
8181
raise ImportError("No module named %s" % mod_name)
8282
if loader.is_package(mod_name):
83-
raise ImportError(("%s is a package and cannot " +
84-
"be directly executed") % mod_name)
83+
if mod_name == "__main__" or mod_name.endswith(".__main__"):
84+
raise ImportError(("Cannot use package as __main__ module"))
85+
try:
86+
pkg_main_name = mod_name + ".__main__"
87+
return _get_module_details(pkg_main_name)
88+
except ImportError as e:
89+
raise ImportError(("%s; %r is a package and cannot " +
90+
"be directly executed") %(e, mod_name))
8591
code = loader.get_code(mod_name)
8692
if code is None:
8793
raise ImportError("No code object available for %s" % mod_name)
8894
filename = _get_filename(loader, mod_name)
89-
return loader, code, filename
95+
return mod_name, loader, code, filename
9096

9197

9298
# XXX ncoghlan: Should this be documented and made public?
@@ -101,12 +107,12 @@ def _run_module_as_main(mod_name, set_argv0=True):
101107
__loader__
102108
"""
103109
try:
104-
loader, code, fname = _get_module_details(mod_name)
110+
mod_name, loader, code, fname = _get_module_details(mod_name)
105111
except ImportError as exc:
106112
# Try to provide a good error message
107113
# for directories, zip files and the -m switch
108114
if set_argv0:
109-
# For -m switch, just disply the exception
115+
# For -m switch, just display the exception
110116
info = str(exc)
111117
else:
112118
# For directories/zipfiles, let the user
@@ -127,7 +133,7 @@ def run_module(mod_name, init_globals=None,
127133
128134
Returns the resulting top level namespace dictionary
129135
"""
130-
loader, code, fname = _get_module_details(mod_name)
136+
mod_name, loader, code, fname = _get_module_details(mod_name)
131137
if run_name is None:
132138
run_name = mod_name
133139
pkg_name = mod_name.rpartition('.')[0]

Lib/test/test_cmd_line_script.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def _make_test_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename,
113113
os.unlink(script_name)
114114
#if verbose:
115115
# zip_file = zipfile.ZipFile(zip_name, 'r')
116-
# print 'Contents of %r:' % zip_name
116+
# print('Contents of %r:' % zip_name)
117117
# zip_file.printdir()
118118
# zip_file.close()
119119
return zip_name, os.path.join(zip_name, script_name_in_zip)
@@ -158,6 +158,16 @@ def _check_script(self, script_name, expected_file,
158158
self.assert_(printed_package in data)
159159
self.assert_(printed_argv0 in data)
160160

161+
def _check_import_error(self, script_name, expected_msg,
162+
*cmd_line_switches):
163+
run_args = cmd_line_switches + (script_name,)
164+
exit_code, data = _run_python(*run_args)
165+
if verbose:
166+
print('Output from test script %r:' % script_name)
167+
print(data)
168+
print('Expected output: %r' % expected_msg)
169+
self.assert_(expected_msg in data)
170+
161171
def test_basic_script(self):
162172
with temp_dir() as script_dir:
163173
script_name = _make_test_script(script_dir, 'script')
@@ -182,6 +192,11 @@ def test_directory_compiled(self):
182192
os.remove(script_name)
183193
self._check_script(script_dir, compiled_name, script_dir, '')
184194

195+
def test_directory_error(self):
196+
with temp_dir() as script_dir:
197+
msg = "can't find '__main__.py' in %r" % script_dir
198+
self._check_import_error(script_dir, msg)
199+
185200
def test_zipfile(self):
186201
with temp_dir() as script_dir:
187202
script_name = _make_test_script(script_dir, '__main__')
@@ -195,6 +210,13 @@ def test_zipfile_compiled(self):
195210
zip_name, run_name = _make_test_zip(script_dir, 'test_zip', compiled_name)
196211
self._check_script(zip_name, run_name, zip_name, '')
197212

213+
def test_zipfile_error(self):
214+
with temp_dir() as script_dir:
215+
script_name = _make_test_script(script_dir, 'not_main')
216+
zip_name, run_name = _make_test_zip(script_dir, 'test_zip', script_name)
217+
msg = "can't find '__main__.py' in %r" % zip_name
218+
self._check_import_error(zip_name, msg)
219+
198220
def test_module_in_package(self):
199221
with temp_dir() as script_dir:
200222
pkg_dir = os.path.join(script_dir, 'test_pkg')
@@ -215,6 +237,47 @@ def test_module_in_subpackage_in_zipfile(self):
215237
launch_name = _make_launch_script(script_dir, 'launch', 'test_pkg.test_pkg.script', zip_name)
216238
self._check_script(launch_name, run_name, run_name, 'test_pkg.test_pkg')
217239

240+
def test_package(self):
241+
with temp_dir() as script_dir:
242+
pkg_dir = os.path.join(script_dir, 'test_pkg')
243+
_make_test_pkg(pkg_dir)
244+
script_name = _make_test_script(pkg_dir, '__main__')
245+
launch_name = _make_launch_script(script_dir, 'launch', 'test_pkg')
246+
self._check_script(launch_name, script_name,
247+
script_name, 'test_pkg')
248+
249+
def test_package_compiled(self):
250+
with temp_dir() as script_dir:
251+
pkg_dir = os.path.join(script_dir, 'test_pkg')
252+
_make_test_pkg(pkg_dir)
253+
script_name = _make_test_script(pkg_dir, '__main__')
254+
compiled_name = _compile_test_script(script_name)
255+
os.remove(script_name)
256+
launch_name = _make_launch_script(script_dir, 'launch', 'test_pkg')
257+
self._check_script(launch_name, compiled_name,
258+
compiled_name, 'test_pkg')
259+
260+
def test_package_error(self):
261+
with temp_dir() as script_dir:
262+
pkg_dir = os.path.join(script_dir, 'test_pkg')
263+
_make_test_pkg(pkg_dir)
264+
msg = ("'test_pkg' is a package and cannot "
265+
"be directly executed")
266+
launch_name = _make_launch_script(script_dir, 'launch', 'test_pkg')
267+
self._check_import_error(launch_name, msg)
268+
269+
def test_package_recursion(self):
270+
with temp_dir() as script_dir:
271+
pkg_dir = os.path.join(script_dir, 'test_pkg')
272+
_make_test_pkg(pkg_dir)
273+
main_dir = os.path.join(pkg_dir, '__main__')
274+
_make_test_pkg(main_dir)
275+
msg = ("Cannot use package as __main__ module; "
276+
"'test_pkg' is a package and cannot "
277+
"be directly executed")
278+
launch_name = _make_launch_script(script_dir, 'launch', 'test_pkg')
279+
self._check_import_error(launch_name, msg)
280+
218281

219282
def test_main():
220283
test.support.run_unittest(CmdLineTest)

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,7 @@ Lionel Ulmer
714714
Roger Upole
715715
Michael Urman
716716
Hector Urtubia
717+
Andi Vajda
717718
Atul Varma
718719
Dmitry Vasiliev
719720
Alexandre Vassalotti

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ Core and Builtins
155155
Library
156156
-------
157157

158+
- Issue #4195: The ``runpy`` module (and the ``-m`` switch) now support
159+
the execution of packages by looking for and executing a ``__main__``
160+
submodule when a package name is supplied. Initial patch by Andi
161+
Vajda.
162+
158163
- Issue #1731706: Call Tcl_ConditionFinalize for Tcl_Conditions that will
159164
not be used again (this requires Tcl/Tk 8.3.1), also fix a memory leak in
160165
Tkapp_Call when calling from a thread different than the one that created

0 commit comments

Comments
 (0)