Patch #661719: Expose compilation errors as exceptions on request.
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 @@
 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 @@
                 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 @@
 
 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
 if os.name == "mac":
@@ -30,17 +77,24 @@
     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 @@
     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 "<string>"',
-                                          '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 @@
     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 @@
             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 @@
 Library
 -------
 
+- py_compile has a new 'doraise' flag and a new PyCompileError
+  exception.
+
 - SimpleXMLRPCServer now supports CGI through the CGIXMLRPCRequestHandler
   class.