Home | History | Annotate | Download | only in exceptions
      1 # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
      2 # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
      3 
      4 from email.mime.text import MIMEText
      5 from email.mime.multipart import MIMEMultipart
      6 import smtplib
      7 import time
      8 try:
      9     from socket import sslerror
     10 except ImportError:
     11     sslerror = None
     12 from paste.exceptions import formatter
     13 
     14 class Reporter(object):
     15 
     16     def __init__(self, **conf):
     17         for name, value in conf.items():
     18             if not hasattr(self, name):
     19                 raise TypeError(
     20                     "The keyword argument %s was not expected"
     21                     % name)
     22             setattr(self, name, value)
     23         self.check_params()
     24 
     25     def check_params(self):
     26         pass
     27 
     28     def format_date(self, exc_data):
     29         return time.strftime('%c', exc_data.date)
     30 
     31     def format_html(self, exc_data, **kw):
     32         return formatter.format_html(exc_data, **kw)
     33 
     34     def format_text(self, exc_data, **kw):
     35         return formatter.format_text(exc_data, **kw)
     36 
     37 class EmailReporter(Reporter):
     38 
     39     to_addresses = None
     40     from_address = None
     41     smtp_server = 'localhost'
     42     smtp_username = None
     43     smtp_password = None
     44     smtp_use_tls = False
     45     subject_prefix = ''
     46 
     47     def report(self, exc_data):
     48         msg = self.assemble_email(exc_data)
     49         server = smtplib.SMTP(self.smtp_server)
     50         if self.smtp_use_tls:
     51             server.ehlo()
     52             server.starttls()
     53             server.ehlo()
     54         if self.smtp_username and self.smtp_password:
     55             server.login(self.smtp_username, self.smtp_password)
     56         server.sendmail(self.from_address,
     57                         self.to_addresses, msg.as_string())
     58         try:
     59             server.quit()
     60         except sslerror:
     61             # sslerror is raised in tls connections on closing sometimes
     62             pass
     63 
     64     def check_params(self):
     65         if not self.to_addresses:
     66             raise ValueError("You must set to_addresses")
     67         if not self.from_address:
     68             raise ValueError("You must set from_address")
     69         if isinstance(self.to_addresses, (str, unicode)):
     70             self.to_addresses = [self.to_addresses]
     71 
     72     def assemble_email(self, exc_data):
     73         short_html_version = self.format_html(
     74             exc_data, show_hidden_frames=False)
     75         long_html_version = self.format_html(
     76             exc_data, show_hidden_frames=True)
     77         text_version = self.format_text(
     78             exc_data, show_hidden_frames=False)
     79         msg = MIMEMultipart()
     80         msg.set_type('multipart/alternative')
     81         msg.preamble = msg.epilogue = ''
     82         text_msg = MIMEText(text_version)
     83         text_msg.set_type('text/plain')
     84         text_msg.set_param('charset', 'ASCII')
     85         msg.attach(text_msg)
     86         html_msg = MIMEText(short_html_version)
     87         html_msg.set_type('text/html')
     88         # @@: Correct character set?
     89         html_msg.set_param('charset', 'UTF-8')
     90         html_long = MIMEText(long_html_version)
     91         html_long.set_type('text/html')
     92         html_long.set_param('charset', 'UTF-8')
     93         msg.attach(html_msg)
     94         msg.attach(html_long)
     95         subject = '%s: %s' % (exc_data.exception_type,
     96                               formatter.truncate(str(exc_data.exception_value)))
     97         msg['Subject'] = self.subject_prefix + subject
     98         msg['From'] = self.from_address
     99         msg['To'] = ', '.join(self.to_addresses)
    100         return msg
    101 
    102 class LogReporter(Reporter):
    103 
    104     filename = None
    105     show_hidden_frames = True
    106 
    107     def check_params(self):
    108         assert self.filename is not None, (
    109             "You must give a filename")
    110 
    111     def report(self, exc_data):
    112         text = self.format_text(
    113             exc_data, show_hidden_frames=self.show_hidden_frames)
    114         f = open(self.filename, 'a')
    115         try:
    116             f.write(text + '\n' + '-'*60 + '\n')
    117         finally:
    118             f.close()
    119 
    120 class FileReporter(Reporter):
    121 
    122     file = None
    123     show_hidden_frames = True
    124 
    125     def check_params(self):
    126         assert self.file is not None, (
    127             "You must give a file object")
    128 
    129     def report(self, exc_data):
    130         text = self.format_text(
    131             exc_data, show_hidden_frames=self.show_hidden_frames)
    132         self.file.write(text + '\n' + '-'*60 + '\n')
    133 
    134 class WSGIAppReporter(Reporter):
    135 
    136     def __init__(self, exc_data):
    137         self.exc_data = exc_data
    138 
    139     def __call__(self, environ, start_response):
    140         start_response('500 Server Error', [('Content-type', 'text/html')])
    141         return [formatter.format_html(self.exc_data)]
    142