From 0c6774d92b48c4c4097a3a3bea2294093092e4e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Wed, 15 Jan 2003 11:51:06 +0000 Subject: Patch #661719: Expose compilation errors as exceptions on request. --- Doc/lib/libpycompile.tex | 9 +++-- Lib/compileall.py | 11 ++---- Lib/py_compile.py | 92 +++++++++++++++++++++++++++++++++++++++--------- Lib/zipfile.py | 5 ++- Misc/NEWS | 3 ++ 5 files changed, 92 insertions(+), 28 deletions(-) diff --git a/Doc/lib/libpycompile.tex b/Doc/lib/libpycompile.tex index a28b4fe..0458191 100644 --- a/Doc/lib/libpycompile.tex +++ b/Doc/lib/libpycompile.tex @@ -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 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 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} \code{+} \code{'c'} (\code{'o'} if optimization is enabled in the current interpreter). If \var{dfile} is specified, it is used as 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} - \begin{funcdesc}{main}{\optional{args}} Compile several source files. The files named in \var{args} (or on the command line, if \var{args} is not specified) are compiled and diff --git a/Lib/compileall.py b/Lib/compileall.py index 8397c96..0f4010f 100644 --- a/Lib/compileall.py +++ b/Lib/compileall.py @@ -62,16 +62,11 @@ def compile_dir(dir, maxlevels=10, ddir=None, if not quiet: print 'Compiling', fullname, '...' try: - ok = py_compile.compile(fullname, None, dfile) + ok = py_compile.compile(fullname, None, dfile, True) except KeyboardInterrupt: raise KeyboardInterrupt - except: - # XXX py_compile catches SyntaxErrors - 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 + except py_compile.PyCompileError,err: + print err.msg success = 0 else: if ok == 0: diff --git a/Lib/py_compile.py b/Lib/py_compile.py index 2d5c36a..95d6a08 100644 --- a/Lib/py_compile.py +++ b/Lib/py_compile.py @@ -12,7 +12,54 @@ import traceback 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 ""', '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 if os.name == "mac": @@ -30,17 +77,24 @@ def wr_long(f, x): f.write(chr((x >> 16) & 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. Arguments: - file: source filename - cfile: target filename; defaults to source with 'c' or 'o' appended - ('c' normally, 'o' in optimizing mode, giving .pyc or .pyo) - dfile: purported filename; defaults to source (this is the filename - that will show up in error messages) - + file: source filename + cfile: target filename; defaults to source with 'c' or 'o' appended + ('c' normally, 'o' in optimizing mode, giving .pyc or .pyo) + dfile: purported filename; defaults to source (this is the filename + 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 execution efficiency -- Python itself byte-compiles a module when 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': codestring = codestring + '\n' try: - codeobject = __builtin__.compile(codestring, dfile or file, 'exec') - except SyntaxError, detail: - lines = traceback.format_exception_only(SyntaxError, detail) - for line in lines: - sys.stderr.write(line.replace('File ""', - 'File "%s"' % (dfile or file))) - return + codeobject = __builtin__.compile(codestring, dfile or file,'exec') + except Exception,err: + py_exc = PyCompileError(err.__class__,err.args,dfile or file) + if doraise: + raise py_exc + else: + sys.stderr.write(py_exc.msg) + return if cfile is None: cfile = file + (__debug__ and 'c' or 'o') fc = open(cfile, 'wb') @@ -100,7 +155,10 @@ def main(args=None): if args is None: args = sys.argv[1:] for filename in args: - compile(filename) - + try: + compile(filename, doraise=True) + except PyCompileError,err: + sys.stderr.write(err.msg) + if __name__ == "__main__": main() diff --git a/Lib/zipfile.py b/Lib/zipfile.py index 6aed172..80d7925 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -604,7 +604,10 @@ class PyZipFile(ZipFile): import py_compile if self.debug: 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 else: fname = file_pyc diff --git a/Misc/NEWS b/Misc/NEWS index 61caf76..30b4902 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -78,6 +78,9 @@ Extension modules Library ------- +- py_compile has a new 'doraise' flag and a new PyCompileError + exception. + - SimpleXMLRPCServer now supports CGI through the CGIXMLRPCRequestHandler class. -- cgit v0.12