Coverage for gws-app / gws / base / web / wsgi_app.py: 60%
114 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-03 10:12 +0100
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-03 10:12 +0100
1"""web application root"""
3import gws
4import gws.base.web
5import gws.config
7_STATE = {
8 'inited': False,
9}
12def application(environ, start_response):
13 if not _STATE['inited']:
14 init()
15 root = gws.config.get_root()
16 responder = handle_request(root, environ)
17 return responder.send_response(environ, start_response)
20def make_application(root):
21 def fn(environ, start_response):
22 responder = handle_request(root, environ)
23 return responder.send_response(environ, start_response)
25 return fn
28def init():
29 try:
30 gws.log.info('initializing WEB application')
31 gws.log.set_level('DEBUG')
32 root = gws.config.load()
33 gws.log.set_level(root.app.cfg('server.log.level'))
34 _STATE['inited'] = True
35 except:
36 gws.log.exception('UNABLE TO LOAD CONFIGURATION')
37 gws.u.exit(1)
40def reload():
41 _STATE['inited'] = False
42 init()
45def handle_request(root: gws.Root, environ) -> gws.WebResponder:
46 site = root.app.webMgr.site_from_environ(environ)
47 req = gws.base.web.wsgi.Requester(root, environ, site)
49 try:
50 req.parse()
51 except Exception as exc:
52 return handle_error(req, exc)
54 gws.log.if_debug(_debug_repr, f'REQUEST_BEGIN {req.command()}', req.params() or req.struct())
55 gws.debug.time_start(f'REQUEST {req.command()}')
56 res = apply_middleware(root, req)
57 gws.debug.time_end()
58 gws.log.if_debug(_debug_repr, f'REQUEST_END {req.command()}', res)
60 return res
63def apply_middleware(root: gws.Root, req: gws.WebRequester) -> gws.WebResponder:
64 res = None
65 done = []
67 for obj in root.app.middlewareMgr.objects():
68 try:
69 res = obj.enter_middleware(req)
70 done.append(obj)
71 except Exception as exc:
72 res = handle_error(req, exc)
74 if res:
75 break
77 if not res:
78 try:
79 m = req.method
80 if m == gws.RequestMethod.GET or m == gws.RequestMethod.POST:
81 res = handle_action(root, req)
82 elif m == gws.RequestMethod.HEAD or m == gws.RequestMethod.OPTIONS:
83 res = req.content_responder(gws.ContentResponse(mime='text/plain', content=''))
84 else:
85 raise gws.base.web.error.MethodNotAllowed(['GET', 'POST', 'HEAD', 'OPTIONS'])
86 except Exception as exc:
87 res = handle_error(req, exc)
89 for obj in reversed(done):
90 try:
91 obj.exit_middleware(req, res)
92 except Exception as exc:
93 res = handle_error(req, exc)
95 return res
98def _debug_repr(prefix, s):
99 s = repr(gws.u.to_dict(s))
100 m = 400
101 n = len(s)
102 if n <= m:
103 return prefix + ': ' + s
104 return prefix + ': ' + s[:m] + ' [...' + str(n - m) + ' more]'
107def handle_error(req: gws.WebRequester, exc: Exception) -> gws.WebResponder:
108 gws.log.if_debug(_debug_repr, f'REQUEST_ERROR', exc)
109 web_exc = gws.base.web.error.from_exception(exc)
110 return handle_http_error(req, web_exc)
113def handle_http_error(req: gws.WebRequester, exc: gws.base.web.error.HTTPException) -> gws.WebResponder:
114 #
115 # @TODO: image errors
117 if req.isApi:
118 return req.api_responder(
119 gws.Response(
120 status=exc.code,
121 error=gws.ResponseError(
122 code=exc.code,
123 info=gws.u.get(exc, 'description', ''),
124 ),
125 )
126 )
128 if not req.site.errorPage:
129 return req.error_responder(exc)
131 args = gws.TemplateArgs(req=req, user=req.user, error=exc.code)
132 res = req.site.errorPage.render(gws.TemplateRenderInput(args=args))
133 res.status = exc.code or 500
134 return req.content_responder(res)
137_relaxed_read_options = {
138 gws.SpecReadOption.caseInsensitive,
139 gws.SpecReadOption.convertValues,
140 gws.SpecReadOption.ignoreExtraProps,
141}
144def handle_action(root: gws.Root, req: gws.WebRequester) -> gws.WebResponder:
145 if not req.command():
146 raise gws.NotFoundError('no command provided')
148 if req.isApi:
149 category = gws.CommandCategory.api
150 params = req.struct()
151 read_options = None
152 elif req.isGet:
153 category = gws.CommandCategory.get
154 params = req.params()
155 read_options = _relaxed_read_options
156 elif req.isPost:
157 category = gws.CommandCategory.post
158 params = req.params()
159 read_options = _relaxed_read_options
160 else:
161 # @TODO: add HEAD
162 raise gws.base.web.error.MethodNotAllowed()
164 fn, request = root.app.actionMgr.prepare_action(category, req.command(), params, req.user, read_options)
166 response = fn(req, request)
168 if response is None:
169 raise gws.NotFoundError(f'action not handled {category!r}:{req.command()!r}')
171 if isinstance(response, gws.ContentResponse):
172 return req.content_responder(response)
174 if isinstance(response, gws.RedirectResponse):
175 return req.redirect_responder(response)
177 return req.api_responder(response)