Coverage for gws-app/gws/base/web/wsgi_app.py: 60%

108 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-16 22:59 +0200

1"""web application root""" 

2 

3import gws 

4import gws.base.web 

5import gws.config 

6 

7_STATE = { 

8 'inited': False, 

9} 

10 

11 

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) 

18 

19 

20def make_application(root): 

21 def fn(environ, start_response): 

22 responder = handle_request(root, environ) 

23 return responder.send_response(environ, start_response) 

24 

25 return fn 

26 

27 

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) 

38 

39 

40def reload(): 

41 _STATE['inited'] = False 

42 init() 

43 

44 

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) 

48 

49 try: 

50 _ = req.params() # enforce parsing 

51 except Exception as exc: 

52 return handle_error(req, exc) 

53 

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) 

59 

60 return res 

61 

62 

63def apply_middleware(root: gws.Root, req: gws.WebRequester) -> gws.WebResponder: 

64 res = None 

65 done = [] 

66 

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) 

73 

74 if res: 

75 break 

76 

77 if not res: 

78 try: 

79 res = handle_action(root, req) 

80 except Exception as exc: 

81 res = handle_error(req, exc) 

82 

83 for obj in reversed(done): 

84 try: 

85 obj.exit_middleware(req, res) 

86 except Exception as exc: 

87 res = handle_error(req, exc) 

88 

89 return res 

90 

91 

92def _debug_repr(prefix, s): 

93 s = repr(gws.u.to_dict(s)) 

94 m = 400 

95 n = len(s) 

96 if n <= m: 

97 return prefix + ': ' + s 

98 return prefix + ': ' + s[:m] + ' [...' + str(n - m) + ' more]' 

99 

100 

101def handle_error(req: gws.WebRequester, exc: Exception) -> gws.WebResponder: 

102 web_exc = gws.base.web.error.from_exception(exc) 

103 return handle_http_error(req, web_exc) 

104 

105 

106def handle_http_error(req: gws.WebRequester, exc: gws.base.web.error.HTTPException) -> gws.WebResponder: 

107 # 

108 # @TODO: image errors 

109 

110 if req.isApi: 

111 return req.api_responder(gws.Response( 

112 status=exc.code, 

113 error=gws.ResponseError( 

114 code=exc.code, 

115 info=gws.u.get(exc, 'description', '')))) 

116 

117 if not req.site.errorPage: 

118 return req.error_responder(exc) 

119 

120 args = gws.TemplateArgs( 

121 req=req, 

122 user=req.user, 

123 error=exc.code 

124 ) 

125 res = req.site.errorPage.render(gws.TemplateRenderInput(args=args)) 

126 res.status = exc.code 

127 return req.content_responder(res) 

128 

129 

130_relaxed_read_options = { 

131 gws.SpecReadOption.caseInsensitive, 

132 gws.SpecReadOption.convertValues, 

133 gws.SpecReadOption.ignoreExtraProps, 

134} 

135 

136 

137def handle_action(root: gws.Root, req: gws.WebRequester) -> gws.WebResponder: 

138 if not req.command(): 

139 raise gws.NotFoundError('no command provided') 

140 

141 if req.isApi: 

142 category = gws.CommandCategory.api 

143 params = req.struct() 

144 read_options = None 

145 elif req.isGet: 

146 category = gws.CommandCategory.get 

147 params = req.params() 

148 read_options = _relaxed_read_options 

149 elif req.isPost: 

150 category = gws.CommandCategory.post 

151 params = req.params() 

152 read_options = _relaxed_read_options 

153 else: 

154 # @TODO: add HEAD 

155 raise gws.base.web.error.MethodNotAllowed() 

156 

157 fn, request = root.app.actionMgr.prepare_action( 

158 category, 

159 req.command(), 

160 params, 

161 req.user, 

162 read_options 

163 ) 

164 

165 response = fn(req, request) 

166 

167 if response is None: 

168 raise gws.NotFoundError(f'action not handled {category!r}:{req.command()!r}') 

169 

170 if isinstance(response, gws.ContentResponse): 

171 return req.content_responder(response) 

172 

173 if isinstance(response, gws.RedirectResponse): 

174 return req.redirect_responder(response) 

175 

176 return req.api_responder(response)