Source code for bobtail.response
from typing import Dict, List, Optional
import json
from bobtail.options import BaseOptions
from bobtail.exceptions import StaticFileError
from bobtail.static_files import (
AUDIO_FILETYPE ,
IMAGE_FILETYPE,
StaticFiles,
TXT_FILETYPE,
VIDEO_FILETYPE,
)
[docs]
class Response:
"""
The :class:`Response` object is available from within a Route method.
"""
status: int = 200
body: Dict = None
html: str = None
static: bytes = None
options: BaseOptions
headers: Dict = {
"Content-Type": "application/json",
}
_content_length: int = None
def __init__(self, options: BaseOptions):
self.options = options
def _process(self) -> List[bytes]:
"""Process final response"""
if self.body is not None:
resp_data = bytes(json.dumps(self.body, indent=2), "utf-8")
self._set_content_len(resp_data)
return [resp_data]
if self.html is not None:
resp_data = bytes(self.html, "utf-8")
self._set_content_len(resp_data)
return [resp_data]
if self.static is not None:
self._set_content_len(self.static)
return [self.static]
self._set_content_len(None)
return []
def _set_content_len(self, data: Optional[bytes]) -> None:
if self._content_length is None:
con_len = 0 if data is None else len(data)
self.headers = {
**self.headers,
"Content-Length": f"{con_len}",
}
[docs]
def set_status(self, value: int) -> None:
"""
Set the response status.
You can set the status with the Response object's set_status method.
The default status is always set to 200 if there are no errors.
For Example::
def get(self, req: Request, res: Response):
res.set_headers({"Content-type": "text/plain"})
:param value: Sets the response status
"""
self.status = value
[docs]
def set_body(self, value: Dict) -> None:
"""
Set the body of the request.
For example::
def get(self, req: Request, res: Response):
res.set_body({id: 1})
:param value:
"""
self.body = value
[docs]
def set_html(self, template_str: str) -> None:
"""
Renders an HTML string. See :class:`~BaseOptions.TEMPLATE_DIR` for the
template directory path.
For example::
template_str = "<h1>Hello!</h1>"
def get(self, req: Request, res: Response):
res.set_html(template_str)
:param template_str:
"""
self.set_headers({"Content-Type": "text/html"})
self.html = template_str
[docs]
def set_static(self, path: str):
"""
To declare a static route postfix a `*` to the route's path::
from bobtail import Bobtail AbstractRoute, BaseOptions
from bobtail_jinja2 import BobtailJinja2
routes = [
(Static(), "/static/*"),
]
class Options(BaseOptions):
STATIC_DIR = "app/static"
TEMPLATE_DIR = "app/templates"
blog = Bobtail(routes=routes, options=Options())
blog.use(BobtailJinja2(template_dir="app/templates"))
Calling `set_static` from within a route method will render a static
file such as a .css, .js or a media type file. The :class:`~BaseOptions`
class sets the `STATIC_DIR` directory.
For example::
class Static(AbstractRoute):
def get(self, req: Request, res: Response) -> None:
res.set_static(req.path)
You can set the static file path using the :class:`~BaseOptions`.
For example::
class Options(BaseOptions):
STATIC_DIR = "/static"
# Now in a route handler we can access static directory the via options
class Static(AbstractRoute):
def get(self, req: Request, res: Response) -> None:
res.set_static(req.path)
By default, `STATIC_DIR` is set to `/static`, if your static file is nested
within a Python package, for example `app/static` the set as `STATIC_DIR = "app/static"`
To render an image from within a Jinja2 template include the full path including the
static directory name or path. For example::
<!-- if STATIC_DIR = "/static" -->
<body>
<img src="/static/imgs/cat1.jpg" />
</body>
OR without the first forward slash::
<body>
<img src="static/imgs/cat1.jpg" />
</body>
:param path:
"""
if len(path.split(".")) < 1:
return None
path_seg = path.split("/static")
if len(path_seg) <= 1:
return None
path = path_seg[1]
try:
file_suffix = path.split("/")[-1:][0].split(".")[-1:][0]
path = f"{self.options.STATIC_DIR}{path}"
if file_suffix in IMAGE_FILETYPE:
StaticFiles(self, "image").set_headers(file_suffix)
elif file_suffix == "css":
StaticFiles(self, "text").set_headers(file_suffix)
elif file_suffix in VIDEO_FILETYPE:
StaticFiles(self, "video").set_headers(file_suffix)
elif file_suffix in AUDIO_FILETYPE:
StaticFiles(self, "audio").set_headers(file_suffix)
else:
StaticFiles(self, "text").set_headers("plain")
except Exception as exc:
self.set_status(500)
raise StaticFileError(
f"Error getting filetype from static file path - {path}"
) from exc
try:
with open(path, "rb") as f:
file_data = f.read()
f.close()
self.static = file_data
except FileNotFoundError:
self.set_status(404)