lema-basic-web-backend 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- basic_web_backend/__init__.py +86 -0
- basic_web_backend/adapters.py +39 -0
- basic_web_backend/application.py +217 -0
- basic_web_backend/config.py +214 -0
- basic_web_backend/exceptions.py +176 -0
- basic_web_backend/logging_config.py +61 -0
- basic_web_backend/multipart.py +109 -0
- basic_web_backend/request.py +180 -0
- basic_web_backend/response.py +197 -0
- basic_web_backend/routing.py +343 -0
- basic_web_backend/static.py +29 -0
- basic_web_backend/template/__init__.py +3 -0
- basic_web_backend/template/environment.py +110 -0
- basic_web_backend/template/evaluator.py +114 -0
- basic_web_backend/template/lexer.py +127 -0
- basic_web_backend/template/nodes.py +34 -0
- basic_web_backend/template/parser.py +221 -0
- lema_basic_web_backend-0.1.0.dist-info/METADATA +546 -0
- lema_basic_web_backend-0.1.0.dist-info/RECORD +22 -0
- lema_basic_web_backend-0.1.0.dist-info/WHEEL +5 -0
- lema_basic_web_backend-0.1.0.dist-info/licenses/LICENSE +21 -0
- lema_basic_web_backend-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from importlib.metadata import version
|
|
2
|
+
|
|
3
|
+
from .adapters import LemaRequestAdapter, LemaResponseAdapter
|
|
4
|
+
from .application import WebApplication
|
|
5
|
+
from .config import ApplicationConfig
|
|
6
|
+
from .exceptions import (
|
|
7
|
+
AmbiguousRouteError,
|
|
8
|
+
BackendError,
|
|
9
|
+
BadRequest,
|
|
10
|
+
Conflict,
|
|
11
|
+
DuplicateRouteError,
|
|
12
|
+
Forbidden,
|
|
13
|
+
HTTPException,
|
|
14
|
+
InvalidRouteError,
|
|
15
|
+
MethodNotAllowed,
|
|
16
|
+
NotFound,
|
|
17
|
+
PayloadTooLarge,
|
|
18
|
+
RoutingError,
|
|
19
|
+
TemplateError,
|
|
20
|
+
TemplateLoadError,
|
|
21
|
+
TemplateNotFound,
|
|
22
|
+
TemplateRenderError,
|
|
23
|
+
TemplateSyntaxError,
|
|
24
|
+
Unauthorized,
|
|
25
|
+
UndefinedVariableError,
|
|
26
|
+
UnprocessableContent,
|
|
27
|
+
UnsupportedMediaType,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
from .multipart import UploadedFile
|
|
31
|
+
from .request import Request
|
|
32
|
+
from .response import (
|
|
33
|
+
content_response,
|
|
34
|
+
delete_cookie,
|
|
35
|
+
empty_response,
|
|
36
|
+
file_response,
|
|
37
|
+
html_response,
|
|
38
|
+
json_response,
|
|
39
|
+
redirect_response,
|
|
40
|
+
set_cookie,
|
|
41
|
+
text_response,
|
|
42
|
+
)
|
|
43
|
+
from .template.environment import TemplateEnvironment
|
|
44
|
+
|
|
45
|
+
__version__ = version("lema-basic-web-backend")
|
|
46
|
+
|
|
47
|
+
__all__ = [
|
|
48
|
+
"__version__",
|
|
49
|
+
"WebApplication",
|
|
50
|
+
"ApplicationConfig",
|
|
51
|
+
"Request",
|
|
52
|
+
"UploadedFile",
|
|
53
|
+
"LemaRequestAdapter",
|
|
54
|
+
"LemaResponseAdapter",
|
|
55
|
+
"TemplateEnvironment",
|
|
56
|
+
"content_response",
|
|
57
|
+
"html_response",
|
|
58
|
+
"text_response",
|
|
59
|
+
"json_response",
|
|
60
|
+
"file_response",
|
|
61
|
+
"redirect_response",
|
|
62
|
+
"empty_response",
|
|
63
|
+
"set_cookie",
|
|
64
|
+
"delete_cookie",
|
|
65
|
+
"BackendError",
|
|
66
|
+
"RoutingError",
|
|
67
|
+
"DuplicateRouteError",
|
|
68
|
+
"InvalidRouteError",
|
|
69
|
+
"AmbiguousRouteError",
|
|
70
|
+
"HTTPException",
|
|
71
|
+
"BadRequest",
|
|
72
|
+
"Unauthorized",
|
|
73
|
+
"Forbidden",
|
|
74
|
+
"NotFound",
|
|
75
|
+
"MethodNotAllowed",
|
|
76
|
+
"Conflict",
|
|
77
|
+
"PayloadTooLarge",
|
|
78
|
+
"UnsupportedMediaType",
|
|
79
|
+
"UnprocessableContent",
|
|
80
|
+
"TemplateError",
|
|
81
|
+
"TemplateNotFound",
|
|
82
|
+
"TemplateLoadError",
|
|
83
|
+
"TemplateSyntaxError",
|
|
84
|
+
"TemplateRenderError",
|
|
85
|
+
"UndefinedVariableError"
|
|
86
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from .request import Request
|
|
2
|
+
|
|
3
|
+
class LemaRequestAdapter:
|
|
4
|
+
def convert(self, server_request):
|
|
5
|
+
method = server_request.method
|
|
6
|
+
path = server_request.path
|
|
7
|
+
query_string = server_request.query_string
|
|
8
|
+
headers = server_request.headers
|
|
9
|
+
body = server_request.body
|
|
10
|
+
|
|
11
|
+
return Request(method, path, query_string, headers, body)
|
|
12
|
+
|
|
13
|
+
class LemaResponseAdapter:
|
|
14
|
+
def convert(self, backend_response):
|
|
15
|
+
if not isinstance(backend_response, tuple):
|
|
16
|
+
return backend_response
|
|
17
|
+
if len(backend_response) != 3:
|
|
18
|
+
return backend_response
|
|
19
|
+
|
|
20
|
+
body, status_code, headers = backend_response
|
|
21
|
+
|
|
22
|
+
filtered_headers = self._remove_content_length(headers)
|
|
23
|
+
|
|
24
|
+
return body, status_code, filtered_headers
|
|
25
|
+
|
|
26
|
+
def _remove_content_length(self, headers):
|
|
27
|
+
if headers is None:
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
if isinstance(headers, dict):
|
|
31
|
+
return {
|
|
32
|
+
name: value for name, value in headers.items()
|
|
33
|
+
if name.lower() != "content-length"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return [
|
|
37
|
+
(name, value) for name, value in headers
|
|
38
|
+
if name.lower() != "content-length"
|
|
39
|
+
]
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
from html import escape
|
|
2
|
+
from traceback import format_exc
|
|
3
|
+
|
|
4
|
+
from .config import ApplicationConfig
|
|
5
|
+
from .logging_config import create_logger
|
|
6
|
+
from .exceptions import BadRequest, HTTPException, PayloadTooLarge
|
|
7
|
+
from .response import html_response
|
|
8
|
+
from .request import Request
|
|
9
|
+
from .routing import Router
|
|
10
|
+
from .static import StaticFileHandler
|
|
11
|
+
from .template.environment import TemplateEnvironment
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class WebApplication:
|
|
15
|
+
def __init__(self, config=None):
|
|
16
|
+
if config is None:
|
|
17
|
+
config = ApplicationConfig()
|
|
18
|
+
|
|
19
|
+
self.config = config
|
|
20
|
+
|
|
21
|
+
self.logger = create_logger(
|
|
22
|
+
name=self.config.logger_name,
|
|
23
|
+
log_file=self.config.log_file,
|
|
24
|
+
log_level=self.config.log_level,
|
|
25
|
+
log_max_bytes=self.config.log_max_bytes,
|
|
26
|
+
log_backup_count=self.config.log_backup_count
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
self.template_environment = TemplateEnvironment(
|
|
30
|
+
template_folder=self.config.template_folder,
|
|
31
|
+
encoding=self.config.template_encoding,
|
|
32
|
+
autoescape=self.config.template_autoescape,
|
|
33
|
+
cache_enabled=self.config.template_cache,
|
|
34
|
+
auto_reload=self.config.template_auto_reload
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
self.router = Router()
|
|
38
|
+
self.error_handlers = {}
|
|
39
|
+
self.static_handler = None
|
|
40
|
+
|
|
41
|
+
if self.config.static_folder is not None:
|
|
42
|
+
self._register_static_route()
|
|
43
|
+
|
|
44
|
+
def _register_static_route(self):
|
|
45
|
+
static_url_path = ("/" + self.config.static_url_path.strip("/"))
|
|
46
|
+
|
|
47
|
+
if static_url_path == "/":
|
|
48
|
+
route_path = "/<path:filename>"
|
|
49
|
+
else:
|
|
50
|
+
route_path = f"{static_url_path}/<path:filename>"
|
|
51
|
+
|
|
52
|
+
self.static_handler = StaticFileHandler(static_folder=self.config.static_folder)
|
|
53
|
+
self.router.add_route(path=route_path, view=self._serve_static_file, methods=["GET"])
|
|
54
|
+
|
|
55
|
+
def _serve_static_file(self, request, filename):
|
|
56
|
+
return self.static_handler.serve(filename=filename)
|
|
57
|
+
|
|
58
|
+
def route(self, path, methods=None):
|
|
59
|
+
def decorator(view):
|
|
60
|
+
self.router.add_route(path=path, view=view, methods=methods)
|
|
61
|
+
return view
|
|
62
|
+
return decorator
|
|
63
|
+
|
|
64
|
+
def errorhandler(self, status_code):
|
|
65
|
+
if not isinstance(status_code, int) or isinstance(status_code, bool):
|
|
66
|
+
raise TypeError("The error handler status code must be an integer.")
|
|
67
|
+
|
|
68
|
+
if not (400 <= status_code <= 599):
|
|
69
|
+
raise ValueError("The error handler status code must be between 400 and 599.")
|
|
70
|
+
|
|
71
|
+
def decorator(handler):
|
|
72
|
+
if not callable(handler):
|
|
73
|
+
raise TypeError("The error handler must be a callable.")
|
|
74
|
+
|
|
75
|
+
if status_code in self.error_handlers:
|
|
76
|
+
raise ValueError(f"An error handler is already registered for status code {status_code}.")
|
|
77
|
+
|
|
78
|
+
self.error_handlers[status_code] = handler
|
|
79
|
+
return handler
|
|
80
|
+
|
|
81
|
+
return decorator
|
|
82
|
+
|
|
83
|
+
def __call__(self, server_request):
|
|
84
|
+
request = None
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
request = self._convert_request(server_request)
|
|
88
|
+
|
|
89
|
+
self._check_content_length(request)
|
|
90
|
+
|
|
91
|
+
route_match = self.router.match_route(path=request.path, method=request.method)
|
|
92
|
+
|
|
93
|
+
response = route_match.view(request=request, **route_match.parameters)
|
|
94
|
+
|
|
95
|
+
except HTTPException as error:
|
|
96
|
+
response = self._handle_http_exception(error, request=request)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
except Exception as error:
|
|
100
|
+
response = self._handle_internal_error(error, request=request)
|
|
101
|
+
|
|
102
|
+
return self._convert_response(response=response, request=request)
|
|
103
|
+
|
|
104
|
+
def _convert_request(self, server_request):
|
|
105
|
+
request = self.config.request_adapter.convert(server_request)
|
|
106
|
+
|
|
107
|
+
if not isinstance(request, Request):
|
|
108
|
+
raise TypeError(
|
|
109
|
+
"The request adapter must return a Request object."
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
return request
|
|
113
|
+
|
|
114
|
+
def _convert_response(self, response, request=None):
|
|
115
|
+
try:
|
|
116
|
+
return self.config.response_adapter.convert(response)
|
|
117
|
+
except Exception:
|
|
118
|
+
if request is None:
|
|
119
|
+
self.logger.exception("The response adapter failed.")
|
|
120
|
+
else:
|
|
121
|
+
self.logger.exception(
|
|
122
|
+
"The response adapter failed while processing a request: %s %s",
|
|
123
|
+
request.method,
|
|
124
|
+
request.path
|
|
125
|
+
)
|
|
126
|
+
raise
|
|
127
|
+
|
|
128
|
+
def _handle_http_exception(self, error, request=None):
|
|
129
|
+
handler = self.error_handlers.get(error.status_code)
|
|
130
|
+
|
|
131
|
+
if handler is not None:
|
|
132
|
+
try:
|
|
133
|
+
return handler(error)
|
|
134
|
+
except Exception as handler_error:
|
|
135
|
+
return self._handle_internal_error(handler_error, request=request)
|
|
136
|
+
|
|
137
|
+
title = f"{error.status_code} {error.default_message}"
|
|
138
|
+
|
|
139
|
+
body = f"<h1>{escape(title)}</h1>"
|
|
140
|
+
|
|
141
|
+
if error.message != error.default_message:
|
|
142
|
+
body += f"<p>{escape(error.message)}</p>"
|
|
143
|
+
|
|
144
|
+
return html_response(
|
|
145
|
+
body=body,
|
|
146
|
+
status_code=error.status_code,
|
|
147
|
+
headers=error.headers
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def _handle_internal_error(self, error, request=None):
|
|
151
|
+
if request is None:
|
|
152
|
+
self.logger.exception("Unhandled exception while adapting a server request.")
|
|
153
|
+
else:
|
|
154
|
+
self.logger.exception(
|
|
155
|
+
"Unhandled exception while processing a request: %s %s",
|
|
156
|
+
request.method,
|
|
157
|
+
request.path
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
handler = self.error_handlers.get(500)
|
|
161
|
+
|
|
162
|
+
if handler is not None:
|
|
163
|
+
try:
|
|
164
|
+
return handler(error)
|
|
165
|
+
except Exception as handler_error:
|
|
166
|
+
self.logger.exception("The custom 500 error handler failed.")
|
|
167
|
+
|
|
168
|
+
return self._create_internal_error_response(handler_error)
|
|
169
|
+
|
|
170
|
+
return self._create_internal_error_response(error)
|
|
171
|
+
|
|
172
|
+
def _create_internal_error_response(self, error):
|
|
173
|
+
if self.config.debug:
|
|
174
|
+
error_type = type(error).__name__
|
|
175
|
+
error_message = escape(str(error))
|
|
176
|
+
error_traceback = escape(format_exc())
|
|
177
|
+
|
|
178
|
+
return html_response(
|
|
179
|
+
body=(
|
|
180
|
+
f"<h1>500 Internal Server Error</h1>"
|
|
181
|
+
f"<h2>{error_type}: {error_message}</h2>"
|
|
182
|
+
f"<pre>{error_traceback}</pre>"
|
|
183
|
+
),
|
|
184
|
+
status_code=500
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
return html_response(
|
|
188
|
+
body="<h1>500 Internal Server Error</h1>",
|
|
189
|
+
status_code=500
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def _check_content_length(self, request):
|
|
193
|
+
limit = self.config.max_content_length
|
|
194
|
+
|
|
195
|
+
if limit is None:
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
body = request.body
|
|
199
|
+
|
|
200
|
+
if isinstance(body, str):
|
|
201
|
+
body_length = len(body.encode("utf-8"))
|
|
202
|
+
else:
|
|
203
|
+
body_length = len(body)
|
|
204
|
+
|
|
205
|
+
if body_length > limit:
|
|
206
|
+
raise PayloadTooLarge(f"The request body exceeds the {limit}-byte limit.")
|
|
207
|
+
|
|
208
|
+
def render_template(self, template_name, status_code=200, headers=None, charset="utf-8", **context):
|
|
209
|
+
rendered_html = self.template_environment.render(template_name, **context)
|
|
210
|
+
|
|
211
|
+
return html_response(
|
|
212
|
+
body=rendered_html,
|
|
213
|
+
status_code=status_code,
|
|
214
|
+
headers=headers,
|
|
215
|
+
charset=charset
|
|
216
|
+
)
|
|
217
|
+
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import codecs
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from .adapters import LemaRequestAdapter, LemaResponseAdapter
|
|
5
|
+
from .logging_config import LOG_LEVELS
|
|
6
|
+
|
|
7
|
+
def _validate_adapter(adapter, name):
|
|
8
|
+
convert = getattr(adapter, "convert", None)
|
|
9
|
+
|
|
10
|
+
if not callable(convert):
|
|
11
|
+
raise TypeError(f"{name} must have a callable 'convert' method")
|
|
12
|
+
|
|
13
|
+
def _validate_boolean(value, name):
|
|
14
|
+
if not isinstance(value, bool):
|
|
15
|
+
raise TypeError(
|
|
16
|
+
f"{name} must be a boolean."
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _validate_optional_non_negative_integer(
|
|
21
|
+
value,
|
|
22
|
+
name,
|
|
23
|
+
):
|
|
24
|
+
if value is None:
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
_validate_non_negative_integer(
|
|
28
|
+
value=value,
|
|
29
|
+
name=name,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _validate_non_negative_integer(
|
|
34
|
+
value,
|
|
35
|
+
name,
|
|
36
|
+
):
|
|
37
|
+
if (
|
|
38
|
+
not isinstance(value, int)
|
|
39
|
+
or isinstance(value, bool)
|
|
40
|
+
):
|
|
41
|
+
raise TypeError(
|
|
42
|
+
f"{name} must be an integer."
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if value < 0:
|
|
46
|
+
raise ValueError(
|
|
47
|
+
f"{name} cannot be negative."
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _validate_static_url_path(value):
|
|
52
|
+
if not isinstance(value, str):
|
|
53
|
+
raise TypeError(
|
|
54
|
+
"static_url_path must be text."
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if not value:
|
|
58
|
+
raise ValueError(
|
|
59
|
+
"static_url_path cannot be empty."
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
if not value.startswith("/"):
|
|
63
|
+
raise ValueError(
|
|
64
|
+
"static_url_path must start "
|
|
65
|
+
"with '/'."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _validate_encoding(encoding):
|
|
70
|
+
if not isinstance(encoding, str):
|
|
71
|
+
raise TypeError(
|
|
72
|
+
"template_encoding must be text."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
codecs.lookup(encoding)
|
|
77
|
+
except LookupError as error:
|
|
78
|
+
raise ValueError(
|
|
79
|
+
"Unknown template encoding: "
|
|
80
|
+
f"{encoding!r}."
|
|
81
|
+
) from error
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _validate_logger_name(name):
|
|
85
|
+
if not isinstance(name, str):
|
|
86
|
+
raise TypeError(
|
|
87
|
+
"logger_name must be text."
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
if not name.strip():
|
|
91
|
+
raise ValueError(
|
|
92
|
+
"logger_name cannot be empty."
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _validate_log_level(log_level):
|
|
97
|
+
if not isinstance(log_level, str):
|
|
98
|
+
raise TypeError(
|
|
99
|
+
"log_level must be text."
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
normalized_level = log_level.upper()
|
|
103
|
+
|
|
104
|
+
if normalized_level not in LOG_LEVELS:
|
|
105
|
+
raise ValueError(
|
|
106
|
+
f"Unknown log level: {log_level}"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
class ApplicationConfig:
|
|
110
|
+
def __init__(
|
|
111
|
+
self,
|
|
112
|
+
request_adapter=None,
|
|
113
|
+
response_adapter=None,
|
|
114
|
+
debug=False,
|
|
115
|
+
static_folder="static",
|
|
116
|
+
static_url_path="/static",
|
|
117
|
+
max_content_length=None,
|
|
118
|
+
template_folder="templates",
|
|
119
|
+
template_encoding="utf-8",
|
|
120
|
+
template_autoescape=True,
|
|
121
|
+
template_cache=True,
|
|
122
|
+
template_auto_reload=False,
|
|
123
|
+
logger_name="basic_web_backend",
|
|
124
|
+
log_file=None,
|
|
125
|
+
log_level="INFO",
|
|
126
|
+
log_max_bytes=1_000_000,
|
|
127
|
+
log_backup_count=5
|
|
128
|
+
):
|
|
129
|
+
if request_adapter is None:
|
|
130
|
+
request_adapter = LemaRequestAdapter()
|
|
131
|
+
|
|
132
|
+
if response_adapter is None:
|
|
133
|
+
response_adapter = LemaResponseAdapter()
|
|
134
|
+
|
|
135
|
+
_validate_adapter(request_adapter, "request_adapter")
|
|
136
|
+
|
|
137
|
+
_validate_adapter(response_adapter, "response_adapter")
|
|
138
|
+
|
|
139
|
+
_validate_adapter(
|
|
140
|
+
request_adapter,
|
|
141
|
+
"request_adapter",
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
_validate_adapter(
|
|
145
|
+
response_adapter,
|
|
146
|
+
"response_adapter",
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
_validate_boolean(
|
|
150
|
+
debug,
|
|
151
|
+
"debug",
|
|
152
|
+
)
|
|
153
|
+
_validate_boolean(
|
|
154
|
+
template_autoescape,
|
|
155
|
+
"template_autoescape",
|
|
156
|
+
)
|
|
157
|
+
_validate_boolean(
|
|
158
|
+
template_cache,
|
|
159
|
+
"template_cache",
|
|
160
|
+
)
|
|
161
|
+
_validate_boolean(
|
|
162
|
+
template_auto_reload,
|
|
163
|
+
"template_auto_reload",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
_validate_optional_non_negative_integer(
|
|
167
|
+
value=max_content_length,
|
|
168
|
+
name="max_content_length",
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
_validate_static_url_path(
|
|
172
|
+
static_url_path
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
_validate_encoding(
|
|
176
|
+
template_encoding
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
_validate_logger_name(
|
|
180
|
+
logger_name
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
_validate_log_level(
|
|
184
|
+
log_level
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
_validate_non_negative_integer(
|
|
188
|
+
value=log_max_bytes,
|
|
189
|
+
name="log_max_bytes",
|
|
190
|
+
)
|
|
191
|
+
_validate_non_negative_integer(
|
|
192
|
+
value=log_backup_count,
|
|
193
|
+
name="log_backup_count",
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
self.request_adapter = request_adapter
|
|
197
|
+
self.response_adapter = response_adapter
|
|
198
|
+
self.debug = debug
|
|
199
|
+
self.template_folder = template_folder
|
|
200
|
+
self.template_encoding = template_encoding
|
|
201
|
+
self.template_autoescape = template_autoescape
|
|
202
|
+
self.template_cache = template_cache
|
|
203
|
+
self.template_auto_reload = template_auto_reload
|
|
204
|
+
self.static_folder = static_folder
|
|
205
|
+
self.static_url_path = static_url_path
|
|
206
|
+
self.max_content_length = max_content_length
|
|
207
|
+
|
|
208
|
+
self.logger_name = logger_name
|
|
209
|
+
self.log_file = log_file
|
|
210
|
+
self.log_level = log_level
|
|
211
|
+
self.log_max_bytes = log_max_bytes
|
|
212
|
+
self.log_backup_count = log_backup_count
|
|
213
|
+
|
|
214
|
+
|