text: import logging
  import logging.config
  from inspect import stack, getmodulename
  from traceback import format_exc
  
  from kaira.log import log
  from kaira.config import Config, LOGGING
  from kaira.router import RouteHandler, PathRouter, RouteMethodHandler
  from kaira.server import run
  from kaira.wrapper import WrapWithContextManager
  from kaira.request import HTTPRequest, HTTP_METHODS
  from kaira.exceptions import BaseExceptionHTTP, ERR_DETAIL, ERR_CSS, ERR_TEMPLATE, \
      HTTPNotFound, HTTPMethodNotAllowed, HTTPError
  from kaira.response import response as kaira_response
  from kaira.statics import StaticHandler
  
  
  class App:
  
      error_routes = dict()
      static_routes = dict()
  
      def __init__(self,
                   name=None,
                   router=None,
                   load_env=True,
                   log_config=LOGGING):
          if log_config:
              logging.config.dictConfig(log_config)
          # Only set up a default log handler if the
          # end-user application didn't set anything up.
          if not (logging.root.handlers and
                  log.level == logging.NOTSET and
                  log_config):
              formatter = logging.Formatter(
                  "%(asctime)s: %(levelname)s: %(message)s")
              handler = logging.StreamHandler()
              handler.setFormatter(formatter)
              log.addHandler(handler)
              log.setLevel(logging.INFO)
  
          # Get name from previous stack frame
          if name is None:
              frame_records = stack()[1]
              name = getmodulename(frame_records[1])
  
          self.name = name
          self.config = Config(load_env=load_env)
          self.router = router or PathRouter()
          self.debug = None
          self.static_handler = None
  
      def static(self, route, path):
          """ get static"""
  
          if not self.static_handler:  # first time
              self.static_handler = StaticHandler(path, prefix=route)
              self.static_routes[route] = path
          else:
              if route not in self.static_routes:
                  self.static_handler.add_files(path, prefix=route)
                  self.static_routes[route] = path
  
      def error(self, status_code):
          """
              @app.error(500)
              def e_500():
                  return 'Hello World'        
          """
  
          if 400 <= status_code <= 505:
              pass
          else:
              raise ValueError("Status code must be 400 <= code <= 505")
  
          def decorator(f):
              self.error_routes[status_code] = f
              return f
  
          return decorator
  
      def path_for(self, name, **kwargs):
          """ path for """
  
          return self.router.path_for(name, **kwargs)
  
      def route(self, path,
                name=None,
                methods=frozenset({"GET"}),
                context=None,
                on_request=None,
                on_response=None):
          """
              @app.route('/')
              def index():
                  return 'Hello World'        
          """
  
          def decorator(f):
              self.add_url_rule(path,
                                name=name,
                                handler=f,
                                methods=methods,
                                context=context,
                                on_request=on_request,
                                on_response=on_response)
              return f
  
          return decorator
  
      def add_url_rule(self, path,
                       name=None,
                       handler=None,
                       methods=frozenset({"GET"}),
                       context=None,
                       on_request=None,
                       on_response=None):
          """ """
  
          if not path.endswith('/'):
              path += '/'
  
          methods = frozenset(map(lambda x: x.upper(), methods))
  
          if not frozenset(methods).issubset(frozenset(HTTP_METHODS)):
              raise ValueError("Not valid method. Only ('GET', 'POST', 'PUT', "
                               "'HEAD', 'OPTIONS', 'PATCH', 'DELETE')")
  
          wrapped_func = WrapWithContextManager(context)(handler)
  
          # if already exist
          match_obj, match_dict = self.router.match(path)
          if match_obj:
              route_hand = match_obj
          else:
              route_hand = RouteHandler(path=path,
                                        name=name,
                                        route_func=handler,
                                        methods=methods
                                        )
  
          for method in methods:
              route_method = RouteMethodHandler(func=wrapped_func,
                                                method=method,
                                                context=context,
                                                on_request=on_request,
                                                on_response=on_response)
              route_hand.handlers[method] = route_method
  
          if not match_obj:
              self.router.add_route(path, route_hand, name=route_hand.name)
  
          log.info('Route added. Path: %s Name: %s' % (path, route_hand.name))
  
      def dynamic_handler(self, environ, start_response):
          """Dynamic handler"""
  
          request = HTTPRequest(environ, 'utf-8')
          request.app = self
  
          try:
              response = self.run_dispatcher(request)
          except HTTPError as e:
              if e.content_type in ['text']:
                  response = kaira_response.text(e.detail, e.status_code,
                                                 cookies=e.cookies, headers=e.headers)
              elif e.content_type in ['json']:
                  response = kaira_response.json(e.detail, e.status_code,
                                                 cookies=e.cookies, headers=e.headers)
  
          except BaseExceptionHTTP as e:
              if 400 <= e.status_code <= 505:
                  if e.status_code in self.error_routes:
                      err_func = self.error_routes[e.status_code]
                      response = err_func(request)
                  else:
                      err_css = ERR_CSS
                      err_detail = ERR_DETAIL[e.status_code]
                      err_code = e.status_code
                      err_explain = 'Oops error ocurred!.'
                      html_template = ERR_TEMPLATE.format(status_code=err_code,
                                                          status_detail=err_detail,
                                                          status_explain=err_explain,
                                                          err_css=err_css)
                      response = kaira_response.html(html_template, status_code=e.status_code)
              elif 300 <= e.status_code <= 307:
                  response = kaira_response.redirect(e.absolute_url, e.status_code,
                                                     cookies=e.cookies, headers=e.headers)
              else:
                  raise NotImplemented("Invalid status code only 300 or 400 http codes")
  
          except Exception as e:
              err_code = 500
              err_css = ERR_CSS
              err_detail = ERR_DETAIL[err_code]
              err_explain_debug = "Error while handling error: {}\nStack: {}".format(
                  e, format_exc())
              err_explain = "An error occurred while handling an error"
              if self.debug:
                  err_explain = err_explain_debug
              log.error(err_explain_debug)
              html_template = ERR_TEMPLATE.format(status_code=err_code,
                                                  status_detail=err_detail,
                                                  status_explain=err_explain,
                                                  err_css=err_css)
              response = kaira_response.html(html_template, status_code=err_code)
  
          return response.__call__(start_response)
  
      def run_dispatcher(self, request):
          """ Run dipatcher """
  
          response = None
          if not request.path.endswith('/'):
              request.path += '/'
          match_obj, match_dict = self.router.match(request.path)
          if match_obj:
  
              try:
                  del match_dict['route_name']  # remove route_name key from dictionary
              except KeyError:
                  pass
  
              try:
                  method_handler = match_obj.handlers[request.method]
              except KeyError:
                  raise HTTPMethodNotAllowed()
  
              if method_handler.on_request:
                  for func_on_req in method_handler.on_request:
                      func_on_req(request)
  
              response = method_handler.func(request, **match_dict)
  
              if method_handler.on_response:
                  for func_on_resp in method_handler.on_response:
                      func_on_resp(request, response)
  
          else:
              raise HTTPNotFound()
  
          return response
  
      def serve_static_handler(self, environ, start_response):
          """static handler"""
  
          try:
              response = self.static_handler.serve_static(environ, start_response)
              return response
  
          except BaseExceptionHTTP as e:
              request = HTTPRequest(environ, 'utf-8')
              if 400 <= e.status_code <= 505:
                  if e.status_code in self.error_routes:
                      err_func = self.error_routes[e.status_code]
                      response = err_func(request)
                  else:
                      err_css = ERR_CSS
                      err_detail = ERR_DETAIL[e.status_code]
                      err_code = e.status_code
                      err_explain = 'Oops error ocurred!.'
                      html_template = ERR_TEMPLATE.format(status_code=err_code,
                                                          status_detail=err_detail,
                                                          status_explain=err_explain,
                                                          err_css=err_css)
                      response = kaira_response.html(html_template, status_code=e.status_code)
              elif 300 <= e.status_code <= 307:
                  kaira_response.redirect(e.absolute_url, e.status_code)
              else:
                  raise NotImplemented("Invalid status code only 300 or 400 http codes")
  
              return response.__call__(start_response)
  
          except Exception as e:
              request = HTTPRequest(environ, 'utf-8')
              err_code = 500
              err_css = ERR_CSS
              err_detail = ERR_DETAIL[err_code]
              err_explain_debug = "Error while handling error: {}\nStack: {}".format(
                  e, format_exc())
              err_explain = "An error occurred while handling an error"
              log.error(err_explain)
              if self.debug:
                  err_explain = err_explain_debug
              html_template = ERR_TEMPLATE.format(status_code=err_code,
                                                  status_detail=err_detail,
                                                  status_explain=err_explain,
                                                  err_css=err_css)
              response = kaira_response.html(html_template, status_code=err_code)
  
              return response.__call__(start_response)
  
      def wsgi_app(self, environ, start_response):
          """ """
  
          path_info = environ['PATH_INFO']
          registered_routes = self.static_routes.keys()
          static_match = path_info.startswith(tuple(registered_routes))
  
          if static_match:  # check if visitor has requested a static file
              return self.serve_static_handler(environ, start_response)
          else:  # else call the dynamic handler
              return self.dynamic_handler(environ, start_response)
  
      def __call__(self, environ, start_response):
          """Shortcut for :attr:`wsgi_app`."""
  
          return self.wsgi_app(environ, start_response)
  
      def run(self, debug=True, **kwargs):
  
          self.debug = debug
          log.info("Server started.. listening on host: %s:%s" % (kwargs['host'], kwargs['port']))
          run(self, **kwargs)
  
      def __repr__(self):
          return '<%s %r>' % (
              self.__class__.__name__,
              self.name,
          )
  
  