Patch #661719: Expose compilation errors as exceptions on request.

This commit is contained in:
Martin v. Löwis 2003-01-15 11:51:06 +00:00
parent d69663d300
commit 0c6774d92b
5 changed files with 92 additions and 28 deletions

View File

@ -19,17 +19,22 @@ modules for shared use, especially if some of the users may not have
permission to write the byte-code cache files in the directory permission to write the byte-code cache files in the directory
containing the source code. containing the source code.
\begin{excdesc}{PyCompileError}
Exception raised when an error occurs while attempting to compile the file.
\end{excdesc}
\begin{funcdesc}{compile}{file\optional{, cfile\optional{, dfile}}} \begin{funcdesc}{compile}{file\optional{, cfile\optional{, dfile\optional{, doraise}}}}
Compile a source file to byte-code and write out the byte-code cache Compile a source file to byte-code and write out the byte-code cache
file. The source code is loaded from the file name \var{file}. The file. The source code is loaded from the file name \var{file}. The
byte-code is written to \var{cfile}, which defaults to \var{file} byte-code is written to \var{cfile}, which defaults to \var{file}
\code{+} \code{'c'} (\code{'o'} if optimization is enabled in the \code{+} \code{'c'} (\code{'o'} if optimization is enabled in the
current interpreter). If \var{dfile} is specified, it is used as current interpreter). If \var{dfile} is specified, it is used as
the name of the source file in error messages instead of \var{file}. the name of the source file in error messages instead of \var{file}.
If \var{doraise} = True, a PyCompileError is raised when an error is
encountered while compiling \var{file}. If \var{doraise} = False (the default),
an error string is written to sys.stderr, but no exception is raised.
\end{funcdesc} \end{funcdesc}
\begin{funcdesc}{main}{\optional{args}} \begin{funcdesc}{main}{\optional{args}}
Compile several source files. The files named in \var{args} (or on Compile several source files. The files named in \var{args} (or on
the command line, if \var{args} is not specified) are compiled and the command line, if \var{args} is not specified) are compiled and

View File

@ -62,16 +62,11 @@ def compile_dir(dir, maxlevels=10, ddir=None,
if not quiet: if not quiet:
print 'Compiling', fullname, '...' print 'Compiling', fullname, '...'
try: try:
ok = py_compile.compile(fullname, None, dfile) ok = py_compile.compile(fullname, None, dfile, True)
except KeyboardInterrupt: except KeyboardInterrupt:
raise KeyboardInterrupt raise KeyboardInterrupt
except: except py_compile.PyCompileError,err:
# XXX py_compile catches SyntaxErrors print err.msg
if type(sys.exc_type) == type(''):
exc_type_name = sys.exc_type
else: exc_type_name = sys.exc_type.__name__
print 'Sorry:', exc_type_name + ':',
print sys.exc_value
success = 0 success = 0
else: else:
if ok == 0: if ok == 0:

View File

@ -12,7 +12,54 @@ import traceback
MAGIC = imp.get_magic() MAGIC = imp.get_magic()
__all__ = ["compile", "main"] __all__ = ["compile", "main", "PyCompileError"]
class PyCompileError(Exception):
"""Exception raised when an error occurs while attempting to
compile the file.
To raise this exception, use
raise PyCompileError(exc_type,exc_value,file[,msg])
where
exc_type: exception type to be used in error message
type name can be accesses as class variable
'exc_type_name'
exc_value: exception value to be used in error message
can be accesses as class variable 'exc_value'
file: name of file being compiled to be used in error message
can be accesses as class variable 'file'
msg: string message to be written as error message
If no value is given, a default exception message will be given,
consistent with 'standard' py_compile output.
message (or default) can be accesses as class variable 'msg'
"""
def __init__(self, exc_type, exc_value, file, msg=''):
exc_type_name = exc_type.__name__
if exc_type is SyntaxError:
tbtext = ''.join(traceback.format_exception_only(exc_type, exc_value))
errmsg = tbtext.replace('File "<string>"', 'File "%s"' % file)
else:
errmsg = "Sorry: %s: %s" % (exc_type_name,exc_value)
Exception.__init__(self,msg or errmsg,exc_type_name,exc_value,file)
self.exc_type_name = exc_type_name
self.exc_value = exc_value
self.file = file
self.msg = msg or errmsg
def __str__(self):
return self.msg
# Define an internal helper according to the platform # Define an internal helper according to the platform
if os.name == "mac": if os.name == "mac":
@ -30,17 +77,24 @@ def wr_long(f, x):
f.write(chr((x >> 16) & 0xff)) f.write(chr((x >> 16) & 0xff))
f.write(chr((x >> 24) & 0xff)) f.write(chr((x >> 24) & 0xff))
def compile(file, cfile=None, dfile=None): def compile(file, cfile=None, dfile=None, doraise=False):
"""Byte-compile one Python source file to Python bytecode. """Byte-compile one Python source file to Python bytecode.
Arguments: Arguments:
file: source filename file: source filename
cfile: target filename; defaults to source with 'c' or 'o' appended cfile: target filename; defaults to source with 'c' or 'o' appended
('c' normally, 'o' in optimizing mode, giving .pyc or .pyo) ('c' normally, 'o' in optimizing mode, giving .pyc or .pyo)
dfile: purported filename; defaults to source (this is the filename dfile: purported filename; defaults to source (this is the filename
that will show up in error messages) that will show up in error messages)
doraise: flag indicating whether or not an exception should be
raised when a compile error is found. If an exception
occurs and this flag is set to False, a string
indicating the nature of the exception will be printed,
and the function will return to the caller. If an
exception occurs and this flag is set to True, a
PyCompileError exception will be raised.
Note that it isn't necessary to byte-compile Python modules for Note that it isn't necessary to byte-compile Python modules for
execution efficiency -- Python itself byte-compiles a module when execution efficiency -- Python itself byte-compiles a module when
it is loaded, and if it can, writes out the bytecode to the it is loaded, and if it can, writes out the bytecode to the
@ -68,13 +122,14 @@ def compile(file, cfile=None, dfile=None):
if codestring and codestring[-1] != '\n': if codestring and codestring[-1] != '\n':
codestring = codestring + '\n' codestring = codestring + '\n'
try: try:
codeobject = __builtin__.compile(codestring, dfile or file, 'exec') codeobject = __builtin__.compile(codestring, dfile or file,'exec')
except SyntaxError, detail: except Exception,err:
lines = traceback.format_exception_only(SyntaxError, detail) py_exc = PyCompileError(err.__class__,err.args,dfile or file)
for line in lines: if doraise:
sys.stderr.write(line.replace('File "<string>"', raise py_exc
'File "%s"' % (dfile or file))) else:
return sys.stderr.write(py_exc.msg)
return
if cfile is None: if cfile is None:
cfile = file + (__debug__ and 'c' or 'o') cfile = file + (__debug__ and 'c' or 'o')
fc = open(cfile, 'wb') fc = open(cfile, 'wb')
@ -100,7 +155,10 @@ def main(args=None):
if args is None: if args is None:
args = sys.argv[1:] args = sys.argv[1:]
for filename in args: for filename in args:
compile(filename) try:
compile(filename, doraise=True)
except PyCompileError,err:
sys.stderr.write(err.msg)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -604,7 +604,10 @@ class PyZipFile(ZipFile):
import py_compile import py_compile
if self.debug: if self.debug:
print "Compiling", file_py print "Compiling", file_py
py_compile.compile(file_py, file_pyc) try:
py_compile.compile(file_py, file_pyc, None, True)
except py_compile.PyCompileError,err:
print err.msg
fname = file_pyc fname = file_pyc
else: else:
fname = file_pyc fname = file_pyc

View File

@ -78,6 +78,9 @@ Extension modules
Library Library
------- -------
- py_compile has a new 'doraise' flag and a new PyCompileError
exception.
- SimpleXMLRPCServer now supports CGI through the CGIXMLRPCRequestHandler - SimpleXMLRPCServer now supports CGI through the CGIXMLRPCRequestHandler
class. class.