Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(5)

Side by Side Diff: Lib/runpy.py

Issue 14285: Traceback wrong on ImportError while executing a package
Patch Set: Created 4 years, 2 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | Lib/test/test_cmd_line_script.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 """runpy.py - locating and running Python code using the module namespace 1 """runpy.py - locating and running Python code using the module namespace
2 2
3 Provides support for locating and running Python scripts using the Python 3 Provides support for locating and running Python scripts using the Python
4 module namespace instead of the native filesystem. 4 module namespace instead of the native filesystem.
5 5
6 This allows Python code to play nicely with non-filesystem based PEP 302 6 This allows Python code to play nicely with non-filesystem based PEP 302
7 importers when locating support scripts as well as when importing modules. 7 importers when locating support scripts as well as when importing modules.
8 """ 8 """
9 # Written by Nick Coghlan <ncoghlan at gmail.com> 9 # Written by Nick Coghlan <ncoghlan at gmail.com>
10 # to implement PEP 338 (Executing Modules as Scripts) 10 # to implement PEP 338 (Executing Modules as Scripts)
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 fname = script_name if mod_spec is None else mod_spec.origin 92 fname = script_name if mod_spec is None else mod_spec.origin
93 with _TempModule(mod_name) as temp_module, _ModifiedArgv0(fname): 93 with _TempModule(mod_name) as temp_module, _ModifiedArgv0(fname):
94 mod_globals = temp_module.module.__dict__ 94 mod_globals = temp_module.module.__dict__
95 _run_code(code, mod_globals, init_globals, 95 _run_code(code, mod_globals, init_globals,
96 mod_name, mod_spec, pkg_name, script_name) 96 mod_name, mod_spec, pkg_name, script_name)
97 # Copy the globals of the temporary module, as they 97 # Copy the globals of the temporary module, as they
98 # may be cleared when the temporary module goes away 98 # may be cleared when the temporary module goes away
99 return mod_globals.copy() 99 return mod_globals.copy()
100 100
101 # Helper to get the loader, code and filename for a module 101 # Helper to get the loader, code and filename for a module
102 def _get_module_details(mod_name): 102 def _get_module_details(mod_name, error=ImportError):
103 try: 103 try:
104 spec = importlib.util.find_spec(mod_name) 104 spec = importlib.util.find_spec(mod_name)
105 except (ImportError, AttributeError, TypeError, ValueError) as ex: 105 except (ImportError, AttributeError, TypeError, ValueError) as ex:
106 # This hack fixes an impedance mismatch between pkgutil and 106 # This hack fixes an impedance mismatch between pkgutil and
107 # importlib, where the latter raises other errors for cases where 107 # importlib, where the latter raises other errors for cases where
108 # pkgutil previously raised ImportError 108 # pkgutil previously raised ImportError
109 msg = "Error while finding spec for {!r} ({}: {})" 109 msg = "Error while finding spec for {!r} ({}: {})"
110 raise ImportError(msg.format(mod_name, type(ex), ex)) from ex 110 raise error(msg.format(mod_name, type(ex), ex)) from ex
111 if spec is None: 111 if spec is None:
112 raise ImportError("No module named %s" % mod_name) 112 raise error("No module named %s" % mod_name)
113 if spec.submodule_search_locations is not None: 113 if spec.submodule_search_locations is not None:
114 if mod_name == "__main__" or mod_name.endswith(".__main__"): 114 if mod_name == "__main__" or mod_name.endswith(".__main__"):
115 raise ImportError("Cannot use package as __main__ module") 115 raise error("Cannot use package as __main__ module")
116 __import__(mod_name) # Do not catch exceptions initializing package
116 try: 117 try:
117 pkg_main_name = mod_name + ".__main__" 118 pkg_main_name = mod_name + ".__main__"
118 return _get_module_details(pkg_main_name) 119 return _get_module_details(pkg_main_name)
119 except ImportError as e: 120 except ImportError as e:
120 raise ImportError(("%s; %r is a package and cannot " + 121 raise error(("%s; %r is a package and cannot " +
121 "be directly executed") %(e, mod_name)) 122 "be directly executed") %(e, mod_name))
122 loader = spec.loader 123 loader = spec.loader
123 if loader is None: 124 if loader is None:
124 raise ImportError("%r is a namespace package and cannot be executed" 125 raise error("%r is a namespace package and cannot be executed"
125 % mod_name) 126 % mod_name)
126 code = loader.get_code(mod_name) 127 try:
128 code = loader.get_code(mod_name)
129 except ImportError as e:
130 raise error(format(e)) from e
127 if code is None: 131 if code is None:
128 raise ImportError("No code object available for %s" % mod_name) 132 raise error("No code object available for %s" % mod_name)
129 return mod_name, spec, code 133 return mod_name, spec, code
134
135 class _Error(Exception):
136 """Error that _run_module_as_main() should report without a traceback"""
130 137
131 # XXX ncoghlan: Should this be documented and made public? 138 # XXX ncoghlan: Should this be documented and made public?
132 # (Current thoughts: don't repeat the mistake that lead to its 139 # (Current thoughts: don't repeat the mistake that lead to its
133 # creation when run_module() no longer met the needs of 140 # creation when run_module() no longer met the needs of
134 # mainmodule.c, but couldn't be changed because it was public) 141 # mainmodule.c, but couldn't be changed because it was public)
135 def _run_module_as_main(mod_name, alter_argv=True): 142 def _run_module_as_main(mod_name, alter_argv=True):
136 """Runs the designated module in the __main__ namespace 143 """Runs the designated module in the __main__ namespace
137 144
138 Note that the executed module will have full access to the 145 Note that the executed module will have full access to the
139 __main__ namespace. If this is not desirable, the run_module() 146 __main__ namespace. If this is not desirable, the run_module()
140 function should be used to run the module code in a fresh namespace. 147 function should be used to run the module code in a fresh namespace.
141 148
142 At the very least, these variables in __main__ will be overwritten: 149 At the very least, these variables in __main__ will be overwritten:
143 __name__ 150 __name__
144 __file__ 151 __file__
145 __cached__ 152 __cached__
146 __loader__ 153 __loader__
147 __package__ 154 __package__
148 """ 155 """
149 try: 156 try:
150 if alter_argv or mod_name != "__main__": # i.e. -m switch 157 if alter_argv or mod_name != "__main__": # i.e. -m switch
151 mod_name, mod_spec, code = _get_module_details(mod_name) 158 mod_name, mod_spec, code = _get_module_details(mod_name, _Error)
152 else: # i.e. directory or zipfile execution 159 else: # i.e. directory or zipfile execution
153 mod_name, mod_spec, code = _get_main_module_details() 160 mod_name, mod_spec, code = _get_main_module_details(_Error)
154 except ImportError as exc: 161 except _Error as exc:
155 # Try to provide a good error message 162 msg = "%s: %s" % (sys.executable, exc)
156 # for directories, zip files and the -m switch
157 if alter_argv:
158 # For -m switch, just display the exception
159 info = str(exc)
160 else:
161 # For directories/zipfiles, let the user
162 # know what the code was looking for
163 info = "can't find '__main__' module in %r" % sys.argv[0]
164 msg = "%s: %s" % (sys.executable, info)
165 sys.exit(msg) 163 sys.exit(msg)
166 main_globals = sys.modules["__main__"].__dict__ 164 main_globals = sys.modules["__main__"].__dict__
167 if alter_argv: 165 if alter_argv:
168 sys.argv[0] = mod_spec.origin 166 sys.argv[0] = mod_spec.origin
169 return _run_code(code, main_globals, None, 167 return _run_code(code, main_globals, None,
170 "__main__", mod_spec) 168 "__main__", mod_spec)
171 169
172 def run_module(mod_name, init_globals=None, 170 def run_module(mod_name, init_globals=None,
173 run_name=None, alter_sys=False): 171 run_name=None, alter_sys=False):
174 """Execute a module's code without importing it 172 """Execute a module's code without importing it
175 173
176 Returns the resulting top level namespace dictionary 174 Returns the resulting top level namespace dictionary
177 """ 175 """
178 mod_name, mod_spec, code = _get_module_details(mod_name) 176 mod_name, mod_spec, code = _get_module_details(mod_name)
179 if run_name is None: 177 if run_name is None:
180 run_name = mod_name 178 run_name = mod_name
181 if alter_sys: 179 if alter_sys:
182 return _run_module_code(code, init_globals, run_name, mod_spec) 180 return _run_module_code(code, init_globals, run_name, mod_spec)
183 else: 181 else:
184 # Leave the sys module alone 182 # Leave the sys module alone
185 return _run_code(code, {}, init_globals, run_name, mod_spec) 183 return _run_code(code, {}, init_globals, run_name, mod_spec)
186 184
187 def _get_main_module_details(): 185 def _get_main_module_details(error=ImportError):
188 # Helper that gives a nicer error message when attempting to 186 # Helper that gives a nicer error message when attempting to
189 # execute a zipfile or directory by invoking __main__.py 187 # execute a zipfile or directory by invoking __main__.py
190 # Also moves the standard __main__ out of the way so that the 188 # Also moves the standard __main__ out of the way so that the
191 # preexisting __loader__ entry doesn't cause issues 189 # preexisting __loader__ entry doesn't cause issues
192 main_name = "__main__" 190 main_name = "__main__"
193 saved_main = sys.modules[main_name] 191 saved_main = sys.modules[main_name]
194 del sys.modules[main_name] 192 del sys.modules[main_name]
195 try: 193 try:
196 return _get_module_details(main_name) 194 return _get_module_details(main_name)
197 except ImportError as exc: 195 except ImportError as exc:
198 if main_name in str(exc): 196 if main_name in str(exc):
199 raise ImportError("can't find %r module in %r" % 197 raise error("can't find %r module in %r" %
200 (main_name, sys.path[0])) from exc 198 (main_name, sys.path[0])) from exc
201 raise 199 raise
202 finally: 200 finally:
203 sys.modules[main_name] = saved_main 201 sys.modules[main_name] = saved_main
204 202
205 203
206 def _get_code_from_file(run_name, fname): 204 def _get_code_from_file(run_name, fname):
207 # Check for a compiled file first 205 # Check for a compiled file first
208 with open(fname, "rb") as f: 206 with open(fname, "rb") as f:
209 code = read_code(f) 207 code = read_code(f)
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
262 pass 260 pass
263 261
264 262
265 if __name__ == "__main__": 263 if __name__ == "__main__":
266 # Run the module specified as the next command line argument 264 # Run the module specified as the next command line argument
267 if len(sys.argv) < 2: 265 if len(sys.argv) < 2:
268 print("No module specified for execution", file=sys.stderr) 266 print("No module specified for execution", file=sys.stderr)
269 else: 267 else:
270 del sys.argv[0] # Make the requested module sys.argv[0] 268 del sys.argv[0] # Make the requested module sys.argv[0]
271 _run_module_as_main(sys.argv[0]) 269 _run_module_as_main(sys.argv[0])
OLDNEW
« no previous file with comments | « no previous file | Lib/test/test_cmd_line_script.py » ('j') | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+