exception.py :  » Template-Engines » Myghty » Myghty-1.1 » lib » myghty » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Template Engines » Myghty 
Myghty » Myghty 1.1 » lib » myghty » exception.py
# $Id: exception.py 2136 2006-09-08 19:46:55Z dairiki $
# exception.py - exception classes for Myghty
# Copyright (C) 2004, 2005 Michael Bayer mike_mp@zzzcomputing.com
# Original Perl code and documentation copyright (c) 1998-2003 by Jonathan Swartz. 
#
# This module is part of Myghty and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
# 

import string, sys, re, traceback, os
from myghty.util import *

# get the directory of myghty, to filter it out of friendly stack traces
import myghty
MYGHTY_DIR = os.path.normcase(os.path.dirname(myghty.__file__))
        
class Error(Exception):
    """generic exception class"""
    
    def __init__(self, message=None, wrapped=None, *args, **params):
        self.wrapped = wrapped

        if message is None:
            if self.wrapped is None:
                self.msg = self.__class__.__name__
            else:
                self.msg = "%s(%s): %s" % (
                    self.__class__.__name__,
                    self.wrapped.__class__.__name__,
                    self.wrapped)
        else:
            self.msg = self.__class__.__name__ + ": " + message

        self.trace_records = []
        self.lead_rec = None
        
        self.reversefiles = {}

        if self.wrapped is not None and isinstance(self.wrapped, SyntaxError):
            self.lead_rec = Error.TBTraceLine(Error.SimpleTraceLine(self.wrapped.text, self.wrapped.lineno, self.wrapped.filename))
            self.ispythonsyntax = True
        else:
            self.ispythonsyntax = False
            
        Exception.__init__(self, *args, **params)
        
        
    def initTraceback(self, interpreter = None):
        """ call this right before each re-raise of the exception"""

        # at the moment, initTraceback only needs to be called once, so
        # return if we already got trace records
        if len(self.trace_records): return

        # extract stack info from the exception throw:
        (type, value, trcback) = sys.exc_info()
        self.raw_excinfo = (type, value, trcback)
        rawrecords = traceback.extract_tb(trcback)

        # add to that, stack info from the current stack frame
        rawrecords = traceback.extract_stack() + rawrecords

        # if a lead record was programmatically hardcoded, init that
        # (it is probably a SyntaxTraceLine from Lexer)
        # and dont change it
        if self.lead_rec is not None:
            self.lead_rec.init_reverse_file(self, interpreter)
            set_lead_rec = False
        else:
            set_lead_rec = True
            
        recs = []
        
        # create TBTraceLines out of the stack frames in an attempt to sort out
        # compiled templatelines, myghty library lines, and non-myghty python lines
        for rec in rawrecords:
            rec = Error.TBTraceLine(Error.SimpleTraceLine(rec[3], rec[1], rec[0]))
            rec.init_reverse_file(self, interpreter)
            
            if not rec.is_myghty_lib() and set_lead_rec:
                self.lead_rec = rec
            recs.insert(0, rec)
        
        self.trace_records += recs

    class CodeLine:
        """represents a line of code in a python source file"""
        def __init__(self, linenum, line, encoding=None):
            self.linenum = linenum
            if line is None:
                self.line = 'None'
            else:
                self.line = _EncodedLine(string.rstrip(line), encoding)

    class TraceLine:
        """represents a trace line in a stack trace"""
        
        def __init__(self):
            self.codeline = None
            self.code = None
            self.file = None
            self.original = None
            
        def init_reverse_file(self, parent, interpreter):raise NotImplementedError()



        def is_myghty_lib(self):
            "returns True if this line corresponds to package in the myghty distribution"
            return self.has_file() and os.path.commonprefix([MYGHTY_DIR, os.path.normcase(self.file)]) == MYGHTY_DIR
            

        def get_lines(self, span = 3):raise NotImplementedError()
        
        def get_file_lines(self, f, linenum, span):
            i = 0
            lines = []
            encoding = self.get_source_encoding()

            while True:
                l = f.readline()
                i += 1
                if i in range(linenum - span, linenum + span + 1):
                    lines.append(Error.CodeLine(i, l, encoding))
                if i == linenum + span: break

            f.close()
            return lines
        
        def has_file(self):
            return self.file is not None and os.access(self.file, os.F_OK)

        def get_source_encoding(self):
            raise NotImplementedError

        
        
    class SyntaxTraceLine(TraceLine):
        """a traceline generated by Lexer to mark a pre-python syntax error"""
        
        def __init__(self, code, line, name, source, file,
                     source_encoding=None):
            self.code = code
            self.line = line
            self.name = name
            self.file = file
            self.source = source
            self.init = False
            self.original = self
            self.codeline = self.line
            if source_encoding is None:
                source_encoding = sys.getdefaultencoding()
            self.source_encoding = source_encoding

        def init_reverse_file(self, parent, interpreter):
            pass

        def get_lines(self, span = 3):
            f = StringIO(self.source)
            return self.get_file_lines(f, self.line, span)

        def get_source_encoding(self):
            return self.source_encoding
        
    class SimpleTraceLine(TraceLine):
        """a traceline for a regular python file"""
        
        def __init__(self, code, line, file):
            self.__code = code
            self.line = line
            self.codeline = self.line
            self.original = self
            self.file = file
            self.compiled_record = None
            
        def init_reverse_file(self, parent, interpreter):
            pass
            
        def _get_code(self):
            if self.__code is None:
                return self.get_lines(span = 0)[0].line
            else:
                return self.__code
        code = property(_get_code)      

        def get_lines(self, span = 3):
            if self.compiled_record is not None:
                f = self.compiled_record.get_compiled_source()
            elif not self.has_file():
                return [Error.CodeLine(self.line, self.__code)] #FIXME: encoding
            else:
                f = file(self.file)

            return self.get_file_lines(f, self.line, span)

        def get_source_encoding(self):
            if self.compiled_record is not None:
                f = self.compiled_record.get_compiled_source()
            elif not self.has_file():
                return sys.getdefaultencoding() # FIXME: this is wrong
            else:
                f = file(self.file)
            return _parse_encoding(f)

    class TBTraceLine(TraceLine):
        """a wrapper for a simpletraceline that attempts to map its python string or file to an 
        originating myghty template string or file, with line numbers cross-linked between them."""
        
        def __init__(self, original):
            self.original = original
            
        def init_reverse_file(self, parent, interpreter):
            original = self.original
            file = original.file
            self.ismyghtylib = None
                
            if parent.reversefiles.has_key(file):
                self.reversefile = parent.reversefiles[file]
            elif interpreter is not None and interpreter.reverse_lookup.has_key(file):
                comprec = interpreter.reverse_lookup[file]
                self.reversefile = Error.ReverseFile(interpreter, compiled_record = comprec)
                parent.reversefiles[file] = self.reversefile
            else:
                self.reversefile = None

            if self.reversefile is not None:
                self.codeline = self.reversefile.get_line_number(original.codeline)
                self.original.compiled_record = self.reversefile.compiled_record
                if self.codeline is None:
                    # if we are a compiled template and the line number is within the
                    # area beyond user-defined code, then we are a myghtylib traceline 
                    self.ismyghtylib = True
            else:
                self.codeline = None

            if self.codeline is None:
                self.codeline = original.codeline
                self.reversefile = None


        def is_myghty_lib(self):
            if self.ismyghtylib is not None:
                return self.ismyghtylib
            else:
                return Error.TraceLine.is_myghty_lib(self)

            
        def _get_code(self):
            if self.reversefile is not None:
                return self.get_lines(span = 0)[0].line
            else:
                return self.original.code
                
        code = property(_get_code, lambda s,p:None)
        
        def _get_file(self):
            if (
                self.reversefile is not None
                and 
                self.reversefile.compiled_record.csource.file_path is not None):
                
                return self.reversefile.compiled_record.csource.file_path
            else:
                return self.original.file
                
        file = property(_get_file, lambda s,p:None)

        def get_lines(self, span = 3):
            if self.reversefile is not None:
                return self.get_file_lines(self.reversefile.get_source(), self.codeline, span)
            else:
                #return [Error.CodeLine(self.codeline, self.original.get_code())]
                return self.original.get_lines(span)

        def get_source_encoding(self):
            return self.original.get_source_encoding()
        
    class ReverseFile:
        """represents a python source string or file, a pointer back to
        the myghty template file or string that originated it, and a cross reference of
        all line numbers in the python file that map back to the template file."""
    
        def __init__(self, interpreter, compiled_record):
            self.sourcemap = []
            self.compiled_record = compiled_record
            self.isvalid = False

            f = compiled_record.get_compiled_source()                   
            try:
                line = f.readline()
                match = re.match(r"# File: (\S+) CompilerID: (.*?) Timestamp: (.*?)", line)
                if not match: return

                (self.path, self.compilerid, self.timestamp) =  \
                (match.group(1), match.group(2), match.group(3) )

                self.sourcemap.append(None)
                state = 0
                linecounter = 0

                for line in f:
                    if state == 0:
                        self.sourcemap.append(None)
                        match = re.match(r"\s*#\s*BEGIN BLOCK (\w+)", line)
                        if match:
                            state = 1

                        continue

                    elif state == 1:
                        match = re.match(r"\s*#\s*BEGIN CODE BLOCK", line)
                        if match:
                            state = 2
                            self.sourcemap.append(None)
                            continue
                    elif state == 2:
                        match = re.match(r"\s*#\s*END CODE BLOCK", line)
                        if match:
                            state = 1
                            self.sourcemap.append(None)
                            continue
                    
                        
                    if state == 1 or state == 2:    
                        match = re.match(r"\s*#\s*SOURCE LINE (\w+)", line)
                        if match:
                            self.sourcemap.append(None)
                            linecounter = int(match.group(1))   
                        else:
                            match = re.match(r"\s*#\s*END BLOCK (\w+)", line)
                            if match:
                                self.sourcemap.append(None)
                                state = 0
                            else:
                                self.sourcemap.append(linecounter)
                                if state == 2: linecounter += 1

                self.isvalid = True
            finally:
                f.close()
                
        def get_source(self):
            try:
                return self.compiled_record.csource.get_component_source_file()
            except:
                raise "Cant open file '%s'" % self.compiled_record.csource.file_path
            
        def get_line_number(self, linenum):
            if linenum < len(self.sourcemap):
                return self.sourcemap[linenum - 1]
            else:
                return None


    def htmlformat(self):
        return HTMLErrorFormatter(self).format()

    def textformat(self):
        return PlainTextErrorFormatter(self).format()

    def format(self):
        return PlainTextErrorFormatter(self).format()
        
    def singlelineformat(self):
        if len(self.trace_records):
            return "%s at %s line %d" % (self.msg, self.file, self.codeline)
        else:
            return self.msg
            
    file =property(lambda self: self.trace_records[0].file)
    

    codeline = property(lambda self:self.trace_records[0].codeline)
    
        
            
    def __str__(self):
        return self.singlelineformat()

    def is_python_syntax(self):
        return self.ispythonsyntax
        
    def message(self):
        return self.msg


class ErrorFormatter:
    """base class for an object that returns a string representation of an error"""
    
    def format(self):
        try:
            return self.do_format()
        except Exception, e:
            return "ErrorFormatter had an exception: " + str(e)
        except:
            e = sys.exc_info()[0]
            return "ErrorFormatter had an exception: " + str(e)


class PlainTextErrorFormatter(ErrorFormatter):
    def __init__(self, error):
        self.error = error
        error.initTraceback(None)
        self.records = self.error.trace_records
        self.lead_rec = self.error.lead_rec

    def do_format(self):
        templatetrace = self._format_traceback(self.records, True)
    
        realtrace = self._format_traceback(self.records, False)

        if self.lead_rec is not None:
            ret = (self._format_record(self.error.message(), self.lead_rec, templatetrace) +
                "\n---------------------------------------------\n" +
                               "Original Stack Trace:\n");
                               
            if self.error.is_python_syntax():
                ret += self._format_record(self.error.message(), self.lead_rec.original, realtrace)
            else:
                ret += self._format_record(self.error.message(), self.records[0].original, realtrace)
                
            return ret
        else:
            return self._format_record(self.error.message(), self.records[0], realtrace)

    def _format_traceback(self, records, friendly):
        trace = ""
        for rec in self.records:
            if not friendly:
                rec = rec.original

            if not friendly or not rec.is_myghty_lib():
                trace += "%s:%s\n" % (rec.file, rec.codeline)

        return trace

    def _format_record(self, message, rec, trace):
        frec = (
            message + "\n\n" + 
            "file: %s line %s\n" % (rec.file, rec.codeline)
            )
    
        frec += "\n" + str(rec.code);
        if trace: frec += "\n\n" + trace

        return frec

class HTMLErrorFormatter(ErrorFormatter):
    def do_format(self):
        buffer = StringIO()

        try:            
            self.interp.execute(self.comp,
                                out_buffer=buffer,
                                output_encoding='latin1', # Default for HTML
                                encoding_errors='htmlentityreplace',
                                request_args = argdict(
                records = self.records,
                lead_rec = self.lead_rec,
                error = self.error
            ))
            return buffer.getvalue()
        except Exception, e:
            nestederror = Error(wrapped = e)
        except:
            nestederror = Error(wrapped = sys.exc_info()[0])

        nestederror.initTraceback(self.interp)
        return ("<pre>%s\n\nAdditionally, HTMLErrorFormatter had the following error:\n\n%s</pre>" % 
            (PlainTextErrorFormatter(self.error).format(),
            PlainTextErrorFormatter(nestederror).format()))


    def __init__(self, error):
        import myghty.interp as interp
        self.error = error
        error.initTraceback(None)
        self.records = self.error.trace_records
        self.lead_rec = self.error.lead_rec

        def handle(e, m, **params):
            raise e

        self.interp = interp.Interpreter(error_handler = handle)
        self.comp = self.interp.make_component("""
<%args>
    records
    lead_rec
    error
</%args>
<html>
<head>
    <title>Myghty Template Error</title>
    <style>
        body, td {
            margin: 20px;
            font-family: verdana, sans-serif;
            font-size: 8pt;
        }
        .red {
            color:#FF0000;
        }
        .bold {
            font-weight: bold;
        }
    </style>
</head>
<body>

% if lead_rec is not None:
    <h2>Myghty Template Error</h2>

    <& formatrecord, message = error.message(), lead_rec = lead_rec, records = records, friendly = True &>

    <br/>
    <hr/>
    <h3>Original Stack Trace:</h3>
%   if error.is_python_syntax():
        <& formatrecord, message = error.message(), lead_rec = lead_rec.original, records = records, friendly = False &>
%   else:
        <& formatrecord, message = error.message(), lead_rec = records[0].original, records = records, friendly = False &>
%
% else:
    <& formatrecord, message = error.message(), lead_rec = records[0], records = records, friendly = False &>
%   
</body>
</html>

<%def formatrecord>
<%args>
    message
    lead_rec
    records
    friendly
</%args>

    <table>
    <tr><td class="bold">Error:</td>
    <td><% message |h %></td>
    <tr><td class="bold">File:</td>
    <td><% lead_rec.file %> line <% lead_rec.codeline %></td>
    </tr>
    <tr>
    <td class="bold" valign="top">Context:</td>
    <td>
    <& formatlines, lines = lead_rec.get_lines(), highlight = lead_rec.codeline &>
    </td>
    </tr>
    
    <td class="bold" valign="top">Traceback:</td>
    <td>
    <& formattraceback, records = records, friendly = friendly &>
    </td>
    </tr>
    </table>
</%def>

<%def formatlines>
<%args>
lines
highlight
</%args>

% for line in lines:
%   code = m.apply_escapes( line.line.expandtabs(), 'h' )
%   code = code.replace(' ','&nbsp;')
%   if line.linenum == highlight:
    <span class="red"><% line.linenum %>: <% code %></span><br/>
%   else:
    <% line.linenum %>: <% code %><br/>
%
</%def>

<%def formattraceback>
<%args>
    records
    friendly
</%args>

% for rec in records:
%   if not friendly:
%       rec = rec.original

%   if not friendly or not rec.is_myghty_lib():
        <% rec.file %>:<% rec.codeline %><br/>  
%

</%def>
""", id='exception')

    
class Syntax(Error):
    "invalid syntax in a component"
    def __init__(self, error, comp_name, source_line, line_number,
                 source = None,
                 file = None,
                 source_encoding = None):
        self.error = error
        Error.__init__(self, error)
        self.lead_rec = Error.SyntaxTraceLine(source_line, line_number,
                                              comp_name,
                                              source, file, source_encoding)

class AbortRequest(Exception):
    "request aborted"
    pass
    
class Abort(AbortRequest):
    "HTTP abort called by component"
    def __init__(self, aborted_value, reason):
        self.aborted_value = aborted_value
        self.reason = reason

class Redirected(Abort):
    "redirect called"
    def __init__(self, path, code=301, reason=None):
        self.path = path
        Abort.__init__(self, code, reason)

            
class Decline(AbortRequest):
    "decline called by component"
    def __init__(self, declined_value):
        self.declined_value = declined_value

class ConfigurationError(Error):
    "configuration error"
    pass

class ServerError(Error):
    "server error (500) "
    pass

class Request(Error):
    "request error"
    pass
            
class Compiler(Error):
    "compiler error"
    pass

class Interpreter(Error):
    "interpreter error"
    pass

class MethodNotFound(Error):
    "couldnt find method"
    pass
        
class ComponentNotFound(Error):
    "couldnt find component"
    def __init__(self, msg, resolution_detail, silent = False):
        Error.__init__(self, msg)
        self.resolution_detail = resolution_detail
        self.silent = silent
        
    def message(self):
        if self.resolution_detail is not None:
            return self.msg + " (" + string.join(self.resolution_detail, ', ') + ")"
        else:
            return self.msg

    def create_toplevel(self):
        """converts this ComponentNotFound into a TopLevelNotFound exception, which 
        is used by HTTPHandlers to return a 404 return code."""
        return TopLevelNotFound(self.msg, self.resolution_detail, self.silent)
        
class PathIsDirectory(ComponentNotFound):
    "the specified component path is a directory"
    pass

class TopLevelNotFound(ComponentNotFound):
    "couldnt find top level component"
    pass

class MissingArgument(Error):
    "a required argument was missing from a component call"
    pass
    
class IncompatibleCompiler(Error):
    "wrong compiler"
    pass

################################################################

import codecs, parser

# Regexp to match python magic encoding line
_PYTHON_MAGIC_COMMENT_re = re.compile(
    r'[ \t\f]* \# .* coding[=:][ \t]*([-\w.]+)',
    re.VERBOSE)

def _parse_encoding(fp): 
    """Deduce the encoding of a source file from magic comment. 
   
    It does this in the same way as the `Python interpreter`__ 
   
    .. __: http://docs.python.org/ref/encodings.html 
   
    The ``fp`` argument should be a seekable file object.
    """
    pos = fp.tell()
    fp.seek(0)
    try:
        line1 = fp.readline()
        has_bom = line1.startswith(codecs.BOM_UTF8)
        if has_bom:
            line1 = line1[len(codecs.BOM_UTF8):]

        m = _PYTHON_MAGIC_COMMENT_re.match(line1)
        if not m:
            try:
                parser.suite(line1)
            except SyntaxError:
                # Either it's a real syntax error, in which case the source
                # is not valid python source, or line2 is a continuation of
                # line1, in which case we don't want to scan line2 for a magic
                # comment.
                pass
            else:
                line2 = fp.readline()
                m = _PYTHON_MAGIC_COMMENT_re.match(line2)

        if has_bom:
            if m:
                raise SyntaxError, \
                      "python refuses to compile code with both a UTF8" \
                      " byte-order-mark and a magic encoding comment"
            return 'utf_8'
        elif m:
            return m.group(1)
        else:
            return None
    finally:
        fp.seek(pos)
                

class _EncodedLine(object):
    def __init__(self, line, encoding=None):
        if encoding is None:
            encoding = sys.getdefaultencoding()
        self.line = line
        self.encoding = encoding

    def __str__(self):
        if isinstance(self.line, str):
            return self.line
        return self.line.encode('ascii', 'replace')

    def __unicode__(self):
        if isinstance(self.line, unicode):
            return self.line
        return self.line.decode(self.encoding, 'replace')

    def expandtabs(self, tabsize=8):
        return _EncodedLine(self.line.expandtabs(tabsize), self.encoding)
    
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.