def initialize(self, service=0): self.service_id = service self.db_conn = self.application.db_pool.connect() self.mail = Mail(options.mail_server) self._args = None super(BaseHandler, self).initialize()
class BaseHandler(RequestHandler): logname = 'NamelessHereForEvermore' def write_response(self, status_code, description, data=None, error=None, reason=None, http_code=None): ts = datetime.today().strftime("%Y-%m-%d %H:%M:%S") status = str(status_code) int_status = int(status_code) sig = ''.join((status, description, json.dumps(data), ts, 'Vanzilla')) sig = hashlib.sha1(sig).hexdigest() if not http_code: if int_status in httputil.responses: http_code = int_status else: http_code = 500 if not reason: reason = self._reason self.set_status(http_code, reason=reason) self.my_response = { 'status': int_status, 'description': description, 'sig': sig, 'timestamp': ts, } if data: self.my_response['data'] = data if error: self.my_response['error'] = str(error) self.set_header('Content-Type', 'application/json') self.write(json.dumps(self.my_response)) def initialize(self, service=0): self.service_id = service self.db_conn = self.application.db_pool.connect() self.mail = Mail(options.mail_server) self._args = None super(BaseHandler, self).initialize() def prepare(self): super(BaseHandler, self).prepare() # If the handler is using RecordHTTPMixin, self.reqid # will be set to the id of the relevant http_reqs DB record if hasattr(self, 'reqid'): uid = self.reqid else: uid = uuid.uuid4() self.log = RequestLogger( self.logname, uid, self.application.settings['log_level']) self.client = Client(self.log, self.db_conn, self.service_id) def on_finish(self): super(BaseHandler, self).on_finish() self.db_conn.close() critical_error_codes = [500, 501, 502, 503, 504] status = int(self.my_response['status']) if hasattr(self, 'my_response') and status in critical_error_codes: uri = self.request.uri message = '' if hasattr(self, 'args'): args = pformat(dict(self.args.__dict__.items())) message += 'Arguments: \n' + args + '\n\n' message += 'Response: \n' + pformat(self.my_response) opt = self.application.options self.mail.send(opt.maifrom, opt.mailto, '[Vanzilla-app] %s' % uri, message) def write_error(self, status_code, **kwargs): critical_error_codes = [500, 501, 502, 503, 504] if status_code in critical_error_codes and 'exc_info' in kwargs: current_exc = traceback.format_exc() message = ''.join(( 'Generated by %s \n\n' % self.request.uri, 'Traceback: \n %s \n\n' % current_exc, )) opt = self.application.options self.mail.send(opt.maifrom, opt.mailto, '[Vanzilla-core] Critical Error', message) if self.settings.get("serve_traceback") and "exc_info" in kwargs: # in debug mode, try to send a traceback self.set_header('Content-Type', 'text/plain') for line in traceback.format_exception(*kwargs["exc_info"]): self.write(line) self.finish() else: if 'exc_info' in kwargs: error = None exc = kwargs['exc_info'][1] if hasattr(exc, 'log_message'): error = exc.log_message else: error = str(exc) self.write_response(status_code, description=self._reason, error=error) self.finish() def decode_argument(self, data, name=None): """ this function try to find the encoding charset then decode the data to UNICODE At least we will have the same encoding for all data """ if isinstance(data, str): for enc in ('utf_8', 'latin_1', 'cp1252', 'ascii'): try: data = data.decode(enc) except UnicodeDecodeError: if enc == "ascii": data = data.decode('ascii', errors='ignore') else: break elif hasattr(data, 'items'): for k, v in data.items(): data[k] = self.decode_argument(v) elif hasattr(data, '__getitem__'): for i, datum in enumerate(data): data[i] = self.decode_argument(datum) return data def get_and_check_args(self, arg_specs): """ Parameters arg_specs, list of tuples like: [ (<arg_name>, <regular_expresssion>, <default_value>, <type_function>), ... ] where: arg_name is the name of the argument regular_expression is a regex string against which the argument value will be checked, or None to skip regex check. default_value is the default value if the user did not supply one, or None if the user MUST supply a value. We recommend you use the empty string '' if the arg is optional but doesn't have a meaningful default value. type_function is a function that will be applied to the argument value, most commonly a type conversion function (int, float, etc.), or the value None to get the raw string. **Note that the default value is NOT passed through this function; if you use the type int, supply an int default if you want consistent values.** Example: class MyHandler(BaseHandler): arg_specs = [ ('my_int', r'^[0-9]*$', 0, int), ('json_list', r'^\[([^,]+(,[^,]+)*)?\]', None, json.loads), ('mandatory_arg', r'^.*$', NO_DEFAULT, None), ] Returns ArgTuple object (with arg names as attributes) Example args = get_and_check_args(arg_specs) args.my_int 0 args.json_list None args.mandatory_arg 'some user supplied value' """ names = [] values = [] for name, regex, default, typefunc in arg_specs: names.append(name) if default is NO_DEFAULT: # Special value set in tornado.web meaning no default default = self._ARG_DEFAULT value = self.get_argument(name, default) if regex is not None: if re.search(regex, value) is None: raise ArgumentValueError(name, value, regex) if typefunc is not None: try: value = typefunc(value) except ValueError: raise ArgumentTypeError(name, value, typefunc) values.append(value) ArgTuple = namedtuple('ArgTuple', names) return ArgTuple(values) @property def args(self): """ You can magically access all arguments defined in self.arg_specs through this attribute, for example if you have defined the argument 'amount', you can access it with self.args.amount Argument parsing and checking is done behind the scenes. """ if not self._args and hasattr(self, 'arg_specs'): self._args = self.get_and_check_args(self.arg_specs) return self._args