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

Skip to content

Commit 45fc871

Browse files
committed
Make distutils error messages more helpful (python#11599).
When running external programs such as a C compiler and getting an error code, distutils only prints the program name. With this change, one can get the full command line by setting the DISTUTILS_DEBUG environment variable. This should have no compatibility issues, unless there are tools that depend on the exact format of distutils debug messages.
1 parent 966f2fc commit 45fc871

4 files changed

Lines changed: 57 additions & 22 deletions

File tree

Doc/distutils/setupscript.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,8 @@ include the following code fragment in your :file:`setup.py` before the
685685
DistributionMetadata.download_url = None
686686

687687

688+
.. _debug-setup-script:
689+
688690
Debugging the setup script
689691
==========================
690692

@@ -700,7 +702,8 @@ installation is broken because they don't read all the way down to the bottom
700702
and see that it's a permission problem.
701703

702704
On the other hand, this doesn't help the developer to find the cause of the
703-
failure. For this purpose, the DISTUTILS_DEBUG environment variable can be set
705+
failure. For this purpose, the :envvar:`DISTUTILS_DEBUG` environment variable can be set
704706
to anything except an empty string, and distutils will now print detailed
705-
information what it is doing, and prints the full traceback in case an exception
706-
occurs.
707+
information about what it is doing, dump the full traceback when an exception
708+
occurs, and print the whole command line when an external program (like a C
709+
compiler) fails.

Doc/install/index.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ new goodies to their toolbox. You don't need to know Python to read this
5858
document; there will be some brief forays into using Python's interactive mode
5959
to explore your installation, but that's it. If you're looking for information
6060
on how to distribute your own Python modules so that others may use them, see
61-
the :ref:`distutils-index` manual.
61+
the :ref:`distutils-index` manual. :ref:`debug-setup-script` may also be of
62+
interest.
6263

6364

6465
.. _inst-trivial-install:

Lib/distutils/spawn.py

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import os
1111

1212
from distutils.errors import DistutilsPlatformError, DistutilsExecError
13+
from distutils.debug import DEBUG
1314
from distutils import log
1415

1516
def spawn(cmd, search_path=1, verbose=0, dry_run=0):
@@ -28,6 +29,9 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0):
2829
Raise DistutilsExecError if running the program fails in any way; just
2930
return on success.
3031
"""
32+
# cmd is documented as a list, but just in case some code passes a tuple
33+
# in, protect our %-formatting code against horrible death
34+
cmd = list(cmd)
3135
if os.name == 'posix':
3236
_spawn_posix(cmd, search_path, dry_run=dry_run)
3337
elif os.name == 'nt':
@@ -67,12 +71,16 @@ def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0):
6771
rc = os.spawnv(os.P_WAIT, executable, cmd)
6872
except OSError as exc:
6973
# this seems to happen when the command isn't found
74+
if not DEBUG:
75+
cmd = executable
7076
raise DistutilsExecError(
71-
"command '%s' failed: %s" % (cmd[0], exc.args[-1]))
77+
"command %r failed: %s" % (cmd, exc.args[-1]))
7278
if rc != 0:
7379
# and this reflects the command running but failing
80+
if not DEBUG:
81+
cmd = executable
7482
raise DistutilsExecError(
75-
"command '%s' failed with exit status %d" % (cmd[0], rc))
83+
"command %r failed with exit status %d" % (cmd, rc))
7684

7785
def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0):
7886
executable = cmd[0]
@@ -86,13 +94,17 @@ def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0):
8694
rc = os.spawnv(os.P_WAIT, executable, cmd)
8795
except OSError as exc:
8896
# this seems to happen when the command isn't found
97+
if not DEBUG:
98+
cmd = executable
8999
raise DistutilsExecError(
90-
"command '%s' failed: %s" % (cmd[0], exc.args[-1]))
100+
"command %r failed: %s" % (cmd, exc.args[-1]))
91101
if rc != 0:
92102
# and this reflects the command running but failing
93-
log.debug("command '%s' failed with exit status %d" % (cmd[0], rc))
103+
if not DEBUG:
104+
cmd = executable
105+
log.debug("command %r failed with exit status %d" % (cmd, rc))
94106
raise DistutilsExecError(
95-
"command '%s' failed with exit status %d" % (cmd[0], rc))
107+
"command %r failed with exit status %d" % (cmd, rc))
96108

97109
if sys.platform == 'darwin':
98110
from distutils import sysconfig
@@ -103,8 +115,9 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0):
103115
log.info(' '.join(cmd))
104116
if dry_run:
105117
return
118+
executable = cmd[0]
106119
exec_fn = search_path and os.execvp or os.execv
107-
exec_args = [cmd[0], cmd]
120+
env = None
108121
if sys.platform == 'darwin':
109122
global _cfg_target, _cfg_target_split
110123
if _cfg_target is None:
@@ -125,17 +138,23 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0):
125138
env = dict(os.environ,
126139
MACOSX_DEPLOYMENT_TARGET=cur_target)
127140
exec_fn = search_path and os.execvpe or os.execve
128-
exec_args.append(env)
129141
pid = os.fork()
130142
if pid == 0: # in the child
131143
try:
132-
exec_fn(*exec_args)
144+
if env is None:
145+
exec_fn(executable, cmd)
146+
else:
147+
exec_fn(executable, cmd, env)
133148
except OSError as e:
134-
sys.stderr.write("unable to execute %s: %s\n"
135-
% (cmd[0], e.strerror))
149+
if not DEBUG:
150+
cmd = executable
151+
sys.stderr.write("unable to execute %r: %s\n"
152+
% (cmd, e.strerror))
136153
os._exit(1)
137154

138-
sys.stderr.write("unable to execute %s for unknown reasons" % cmd[0])
155+
if not DEBUG:
156+
cmd = executable
157+
sys.stderr.write("unable to execute %r for unknown reasons" % cmd)
139158
os._exit(1)
140159
else: # in the parent
141160
# Loop until the child either exits or is terminated by a signal
@@ -147,26 +166,34 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0):
147166
import errno
148167
if exc.errno == errno.EINTR:
149168
continue
169+
if not DEBUG:
170+
cmd = executable
150171
raise DistutilsExecError(
151-
"command '%s' failed: %s" % (cmd[0], exc.args[-1]))
172+
"command %r failed: %s" % (cmd, exc.args[-1]))
152173
if os.WIFSIGNALED(status):
174+
if not DEBUG:
175+
cmd = executable
153176
raise DistutilsExecError(
154-
"command '%s' terminated by signal %d"
155-
% (cmd[0], os.WTERMSIG(status)))
177+
"command %r terminated by signal %d"
178+
% (cmd, os.WTERMSIG(status)))
156179
elif os.WIFEXITED(status):
157180
exit_status = os.WEXITSTATUS(status)
158181
if exit_status == 0:
159182
return # hey, it succeeded!
160183
else:
184+
if not DEBUG:
185+
cmd = executable
161186
raise DistutilsExecError(
162-
"command '%s' failed with exit status %d"
163-
% (cmd[0], exit_status))
187+
"command %r failed with exit status %d"
188+
% (cmd, exit_status))
164189
elif os.WIFSTOPPED(status):
165190
continue
166191
else:
192+
if not DEBUG:
193+
cmd = executable
167194
raise DistutilsExecError(
168-
"unknown error executing '%s': termination status %d"
169-
% (cmd[0], status))
195+
"unknown error executing %r: termination status %d"
196+
% (cmd, status))
170197

171198
def find_executable(executable, path=None):
172199
"""Tries to find 'executable' in the directories listed in 'path'.

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ Library
1616
- Issue #20875: Prevent possible gzip "'read' is not defined" NameError.
1717
Patch by Claudiu Popa.
1818

19+
- Issue #11599: When an external command (e.g. compiler) fails, distutils now
20+
prints out the whole command line (instead of just the command name) if the
21+
environment variable DISTUTILS_DEBUG is set.
22+
1923
- Issue #4931: distutils should not produce unhelpful "error: None" messages
2024
anymore. distutils.util.grok_environment_error is kept but doc-deprecated.
2125

0 commit comments

Comments
 (0)