def wsgi(self, environ, start_response): """ The bottle WSGI-interface. """ try: out = self._cast(self._handle(environ)) # rfc2616 section 4.3 if http_wsgi.response._status_code in ( 100, 101, 204, 304) or environ['REQUEST_METHOD'] == 'HEAD': if hasattr(out, 'close'): out.close() out = [] start_response(http_wsgi.response._status_line, http_wsgi.response.headerlist) return out except (KeyboardInterrupt, SystemExit, MemoryError): raise except Exception: if not self.catchall: raise err = '<h1>Critical error while processing request: %s</h1>' \ % http_wsgi.html_escape(environ.get('PATH_INFO', '/')) if settings.DEBUG: err += '<h2>Error:</h2>\n<pre>\n%s\n</pre>\n' \ '<h2>Traceback:</h2>\n<pre>\n%s\n</pre>\n' \ % (http_wsgi.html_escape(repr(settings._e())), http_wsgi.html_escape(settings.format_exc())) environ['wsgi.errors'].write(err) headers = [('Content-Type', 'text/html; charset=UTF-8')] start_response('500 INTERNAL SERVER ERROR', headers, sys.exc_info()) return [settings.tob(err)]
def build(self, _name, *anons, **query): ''' Build an URL by filling the wildcards in a rule. ''' builder = self.builder.get(_name) if not builder: raise RouteBuildError("No route with that name.", _name) try: for i, value in enumerate(anons): query['anon%d' % i] = value url = ''.join([f(query.pop(n)) if n else f for (n, f) in builder]) return url if not query else url + '?' + settings.urlencode(query) except KeyError: raise RouteBuildError('Missing URL argument: %r' % settings._e().args[0])
def _handle(self, environ): path = environ['bottle.raw_path'] = environ['PATH_INFO'] if settings.py3k: try: environ['PATH_INFO'] = path.encode('latin1').decode('utf8') except UnicodeError: return http_wsgi.HTTPError( 400, 'Invalid path string. Expected UTF-8') try: environ['bottle.app'] = self http_wsgi.request.bind(environ) http_wsgi.response.bind() try: self.trigger_hook('before_request') route, args = self.router.match(environ) environ['route.handle'] = route environ['bottle.route'] = route environ['route.url_args'] = args return route.call(**args) finally: self.trigger_hook('after_request') except http_wsgi.HTTPResponse: return settings._e() except routings.RouteReset: route.reset() return self._handle(environ) except (KeyboardInterrupt, SystemExit, MemoryError): raise except Exception: if not self.catchall: raise stacktrace = settings.format_exc() environ['wsgi.errors'].write(stacktrace) return http_wsgi.HTTPError(500, "Internal Server Error", settings._e(), stacktrace)
def wrapper(*a, **ka): try: rv = callback(*a, **ka) except http_wsgi.HTTPError: rv = settings._e() if isinstance(rv, dict): #Attempt to serialize, raises exception on failure json_response = dumps(rv) #Set content type only if serialization succesful http_wsgi.response.content_type = 'application/json' return json_response elif isinstance(rv, http_wsgi.HTTPResponse) and isinstance( rv.body, dict): rv.body = dumps(rv.body) rv.content_type = 'application/json' return rv
def add(self, rule, method, target, name=None): ''' Add a new rule or replace the target for an existing rule. ''' anons = 0 # Number of anonymous wildcards found keys = [] # Names of keys pattern = '' # Regular expression pattern with named groups filters = [] # Lists of wildcard input filters builder = [] # Data structure for the URL builder is_static = True for key, mode, conf in self._itertokens(rule): if mode: is_static = False if mode == 'default': mode = self.default_filter mask, in_filter, out_filter = self.filters[mode](conf) if not key: pattern += '(?:%s)' % mask key = 'anon%d' % anons anons += 1 else: pattern += '(?P<%s>%s)' % (key, mask) keys.append(key) if in_filter: filters.append((key, in_filter)) builder.append((key, out_filter or str)) elif key: pattern += settings.re.escape(key) builder.append((None, key)) self.builder[rule] = builder if name: self.builder[name] = builder if is_static and not self.strict_order: self.static.setdefault(method, {}) self.static[method][self.build(rule)] = (target, None) return try: re_pattern = settings.re.compile('^(%s)$' % pattern) re_match = re_pattern.match except settings.re.error: raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, settings._e())) if filters: def getargs(path): url_args = re_match(path).groupdict() for name, wildcard_filter in filters: try: url_args[name] = wildcard_filter(url_args[name]) except ValueError: raise http_wsgi.HTTPError(400, 'Path has wrong format.') return url_args elif re_pattern.groupindex: def getargs(path): return re_match(path).groupdict() else: getargs = None flatpat = _re_flatten(pattern) whole_rule = (rule, flatpat, target, getargs) if (flatpat, method) in self._groups: if settings.DEBUG: msg = 'Route <%s %s> overwrites a previously defined route' settings.warnings.warn(msg % (method, rule), RuntimeWarning) self.dyna_routes[method][self._groups[flatpat, method]] = whole_rule else: self.dyna_routes.setdefault(method, []).append(whole_rule) self._groups[flatpat, method] = len(self.dyna_routes[method]) - 1 self._compile(method)
def _cast(self, out, peek=None): """ Try to convert the parameter into something WSGI compatible and set correct HTTP headers when possible. Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like, iterable of strings and iterable of unicodes """ # Empty output is done here if not out: if 'Content-Length' not in http_wsgi.response: http_wsgi.response['Content-Length'] = 0 return [] # Join lists of byte or unicode strings. Mixed lists are NOT supported if isinstance(out, (tuple, list)) and isinstance(out[0], (bytes, settings.unicode)): out = out[0][0:0].join(out) # b'abc'[0:0] -> b'' # Encode unicode strings if isinstance(out, settings.unicode): out = out.encode(http_wsgi.response.charset) # Byte Strings are just returned if isinstance(out, bytes): if 'Content-Length' not in http_wsgi.response: http_wsgi.response['Content-Length'] = len(out) return [out] # HTTPError or HTTPException (recursive, because they may wrap anything) # TODO: Handle these explicitly in handle() or make them iterable. if isinstance(out, http_wsgi.HTTPError): out.apply(http_wsgi.response) out = self.error_handler.get(out.status_code, self.default_error_handler)(out) return self._cast(out) if isinstance(out, http_wsgi.HTTPResponse): out.apply(http_wsgi.response) return self._cast(out.body) # File-like objects. if hasattr(out, 'read'): if 'wsgi.file_wrapper' in http_wsgi.request.environ: return http_wsgi.request.environ['wsgi.file_wrapper'](out) elif hasattr(out, 'close') or not hasattr(out, '__iter__'): return commons.WSGIFileWrapper(out) # Handle Iterables. We peek into them to detect their inner type. try: iout = iter(out) first = next(iout) while not first: first = next(iout) except StopIteration: return self._cast('') except http_wsgi.HTTPResponse: first = settings._e() except (KeyboardInterrupt, SystemExit, MemoryError): raise except Exception: if not self.catchall: raise first = http_wsgi.HTTPError(500, 'Unhandled exception', settings._e(), settings.format_exc()) # These are the inner types allowed in iterator or generator objects. if isinstance(first, http_wsgi.HTTPResponse): return self._cast(first) elif isinstance(first, bytes): new_iter = settings.itertools.chain([first], iout) elif isinstance(first, settings.unicode): encoder = lambda x: x.encode(http_wsgi.response.charset) new_iter = settings.imap(encoder, settings.itertools.chain([first], iout)) else: msg = 'Unsupported response type: %s' % type(first) return self._cast(http_wsgi.HTTPError(500, msg)) if hasattr(out, 'close'): new_iter = commons._closeiter(new_iter, out.close) return new_iter