_cpdispatch.py (14021B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\cherrypy\_cpdispatch.py 2 import string 3 import sys 4 import types 5 import cherrypy 6 7 class PageHandler(object): 8 9 def __init__(self, callable, *args, **kwargs): 10 self.callable = callable 11 self.args = args 12 self.kwargs = kwargs 13 14 def __call__(self): 15 try: 16 return self.callable(*self.args, **self.kwargs) 17 except TypeError: 18 x = sys.exc_info()[1] 19 try: 20 test_callable_spec(self.callable, self.args, self.kwargs) 21 except cherrypy.HTTPError: 22 raise sys.exc_info()[1] 23 except: 24 raise x 25 26 raise 27 28 29 def test_callable_spec(callable, callable_args, callable_kwargs): 30 show_mismatched_params = getattr(cherrypy.serving.request, 'show_mismatched_params', False) 31 try: 32 args, varargs, varkw, defaults = inspect.getargspec(callable) 33 except TypeError: 34 if isinstance(callable, object) and hasattr(callable, '__call__'): 35 args, varargs, varkw, defaults = inspect.getargspec(callable.__call__) 36 else: 37 raise 38 39 if args and args[0] == 'self': 40 args = args[1:] 41 arg_usage = dict([ (arg, 0) for arg in args ]) 42 vararg_usage = 0 43 varkw_usage = 0 44 extra_kwargs = set() 45 for i, value in enumerate(callable_args): 46 try: 47 arg_usage[args[i]] += 1 48 except IndexError: 49 vararg_usage += 1 50 51 for key in callable_kwargs.keys(): 52 try: 53 arg_usage[key] += 1 54 except KeyError: 55 varkw_usage += 1 56 extra_kwargs.add(key) 57 58 args_with_defaults = args[-len(defaults or []):] 59 for i, val in enumerate(defaults or []): 60 if arg_usage[args_with_defaults[i]] == 0: 61 arg_usage[args_with_defaults[i]] += 1 62 63 missing_args = [] 64 multiple_args = [] 65 for key, usage in arg_usage.items(): 66 if usage == 0: 67 missing_args.append(key) 68 elif usage > 1: 69 multiple_args.append(key) 70 71 if missing_args: 72 message = None 73 if show_mismatched_params: 74 message = 'Missing parameters: %s' % ','.join(missing_args) 75 raise cherrypy.HTTPError(404, message=message) 76 if not varargs and vararg_usage > 0: 77 raise cherrypy.HTTPError(404) 78 body_params = cherrypy.serving.request.body.params or {} 79 body_params = set(body_params.keys()) 80 qs_params = set(callable_kwargs.keys()) - body_params 81 if multiple_args: 82 if qs_params.intersection(set(multiple_args)): 83 error = 404 84 else: 85 error = 400 86 message = None 87 if show_mismatched_params: 88 message = 'Multiple values for parameters: %s' % ','.join(multiple_args) 89 raise cherrypy.HTTPError(error, message=message) 90 if not varkw and varkw_usage > 0: 91 extra_qs_params = set(qs_params).intersection(extra_kwargs) 92 if extra_qs_params: 93 message = None 94 if show_mismatched_params: 95 message = 'Unexpected query string parameters: %s' % ', '.join(extra_qs_params) 96 raise cherrypy.HTTPError(404, message=message) 97 extra_body_params = set(body_params).intersection(extra_kwargs) 98 if extra_body_params: 99 message = None 100 if show_mismatched_params: 101 message = 'Unexpected body parameters: %s' % ', '.join(extra_body_params) 102 raise cherrypy.HTTPError(400, message=message) 103 104 105 try: 106 import inspect 107 except ImportError: 108 test_callable_spec = lambda callable, args, kwargs: None 109 110 class LateParamPageHandler(PageHandler): 111 112 def _get_kwargs(self): 113 kwargs = cherrypy.serving.request.params.copy() 114 if self._kwargs: 115 kwargs.update(self._kwargs) 116 return kwargs 117 118 def _set_kwargs(self, kwargs): 119 self._kwargs = kwargs 120 121 kwargs = property(_get_kwargs, _set_kwargs, doc='page handler kwargs (with cherrypy.request.params copied in)') 122 123 124 punctuation_to_underscores = string.maketrans(string.punctuation, '_' * len(string.punctuation)) 125 126 class Dispatcher(object): 127 dispatch_method_name = '_cp_dispatch' 128 129 def __init__(self, dispatch_method_name = None, translate = punctuation_to_underscores): 130 if not isinstance(translate, str) or len(translate) != 256: 131 raise ValueError('The translate argument must be a str of len 256.') 132 self.translate = translate 133 if dispatch_method_name: 134 self.dispatch_method_name = dispatch_method_name 135 136 def __call__(self, path_info): 137 request = cherrypy.serving.request 138 func, vpath = self.find_handler(path_info) 139 if func: 140 vpath = [ x.replace('%2F', '/') for x in vpath ] 141 request.handler = LateParamPageHandler(func, *vpath) 142 else: 143 request.handler = cherrypy.NotFound() 144 145 def find_handler(self, path): 146 request = cherrypy.serving.request 147 app = request.app 148 root = app.root 149 dispatch_name = self.dispatch_method_name 150 fullpath = [ x for x in path.strip('/').split('/') if x ] + ['index'] 151 fullpath_len = len(fullpath) 152 segleft = fullpath_len 153 nodeconf = {} 154 if hasattr(root, '_cp_config'): 155 nodeconf.update(root._cp_config) 156 if '/' in app.config: 157 nodeconf.update(app.config['/']) 158 object_trail = [['root', 159 root, 160 nodeconf, 161 segleft]] 162 node = root 163 iternames = fullpath[:] 164 while iternames: 165 name = iternames[0] 166 objname = name.translate(self.translate) 167 nodeconf = {} 168 subnode = getattr(node, objname, None) 169 pre_len = len(iternames) 170 if subnode is None: 171 dispatch = getattr(node, dispatch_name, None) 172 if dispatch and hasattr(dispatch, '__call__') and not getattr(dispatch, 'exposed', False) and pre_len > 1: 173 index_name = iternames.pop() 174 subnode = dispatch(vpath=iternames) 175 iternames.append(index_name) 176 else: 177 iternames.pop(0) 178 else: 179 iternames.pop(0) 180 segleft = len(iternames) 181 if segleft > pre_len: 182 raise cherrypy.CherryPyException('A vpath segment was added. Custom dispatchers may only ' + 'remove elements. While trying to process ' + '{0} in {1}'.format(name, fullpath)) 183 elif segleft == pre_len: 184 iternames.pop(0) 185 segleft -= 1 186 node = subnode 187 if node is not None: 188 if hasattr(node, '_cp_config'): 189 nodeconf.update(node._cp_config) 190 existing_len = fullpath_len - pre_len 191 if existing_len != 0: 192 curpath = '/' + '/'.join(fullpath[0:existing_len]) 193 else: 194 curpath = '' 195 new_segs = fullpath[fullpath_len - pre_len:fullpath_len - segleft] 196 for seg in new_segs: 197 curpath += '/' + seg 198 if curpath in app.config: 199 nodeconf.update(app.config[curpath]) 200 201 object_trail.append([name, 202 node, 203 nodeconf, 204 segleft]) 205 206 def set_conf(): 207 base = cherrypy.config.copy() 208 for name, obj, conf, segleft in object_trail: 209 base.update(conf) 210 if 'tools.staticdir.dir' in conf: 211 base['tools.staticdir.section'] = '/' + '/'.join(fullpath[0:fullpath_len - segleft]) 212 213 return base 214 215 num_candidates = len(object_trail) - 1 216 for i in range(num_candidates, -1, -1): 217 name, candidate, nodeconf, segleft = object_trail[i] 218 if candidate is None: 219 continue 220 if hasattr(candidate, 'default'): 221 defhandler = candidate.default 222 if getattr(defhandler, 'exposed', False): 223 conf = getattr(defhandler, '_cp_config', {}) 224 object_trail.insert(i + 1, ['default', 225 defhandler, 226 conf, 227 segleft]) 228 request.config = set_conf() 229 request.is_index = path.endswith('/') 230 return (defhandler, fullpath[fullpath_len - segleft:-1]) 231 if getattr(candidate, 'exposed', False): 232 request.config = set_conf() 233 if i == num_candidates: 234 request.is_index = True 235 else: 236 request.is_index = False 237 return (candidate, fullpath[fullpath_len - segleft:-1]) 238 239 request.config = set_conf() 240 return (None, []) 241 242 243 class MethodDispatcher(Dispatcher): 244 245 def __call__(self, path_info): 246 request = cherrypy.serving.request 247 resource, vpath = self.find_handler(path_info) 248 if resource: 249 avail = [ m for m in dir(resource) if m.isupper() ] 250 if 'GET' in avail and 'HEAD' not in avail: 251 avail.append('HEAD') 252 avail.sort() 253 cherrypy.serving.response.headers['Allow'] = ', '.join(avail) 254 meth = request.method.upper() 255 func = getattr(resource, meth, None) 256 if func is None and meth == 'HEAD': 257 func = getattr(resource, 'GET', None) 258 if func: 259 if hasattr(func, '_cp_config'): 260 request.config.update(func._cp_config) 261 vpath = [ x.replace('%2F', '/') for x in vpath ] 262 request.handler = LateParamPageHandler(func, *vpath) 263 else: 264 request.handler = cherrypy.HTTPError(405) 265 else: 266 request.handler = cherrypy.NotFound() 267 268 269 class RoutesDispatcher(object): 270 271 def __init__(self, full_result = False): 272 import routes 273 self.full_result = full_result 274 self.controllers = {} 275 self.mapper = routes.Mapper() 276 self.mapper.controller_scan = self.controllers.keys 277 278 def connect(self, name, route, controller, **kwargs): 279 self.controllers[name] = controller 280 self.mapper.connect(name, route, controller=name, **kwargs) 281 282 def redirect(self, url): 283 raise cherrypy.HTTPRedirect(url) 284 285 def __call__(self, path_info): 286 func = self.find_handler(path_info) 287 if func: 288 cherrypy.serving.request.handler = LateParamPageHandler(func) 289 else: 290 cherrypy.serving.request.handler = cherrypy.NotFound() 291 292 def find_handler(self, path_info): 293 import routes 294 request = cherrypy.serving.request 295 config = routes.request_config() 296 config.mapper = self.mapper 297 if hasattr(request, 'wsgi_environ'): 298 config.environ = request.wsgi_environ 299 config.host = request.headers.get('Host', None) 300 config.protocol = request.scheme 301 config.redirect = self.redirect 302 result = self.mapper.match(path_info) 303 config.mapper_dict = result 304 params = {} 305 if result: 306 params = result.copy() 307 if not self.full_result: 308 params.pop('controller', None) 309 params.pop('action', None) 310 request.params.update(params) 311 request.config = base = cherrypy.config.copy() 312 curpath = '' 313 314 def merge(nodeconf): 315 if 'tools.staticdir.dir' in nodeconf: 316 nodeconf['tools.staticdir.section'] = curpath or '/' 317 base.update(nodeconf) 318 319 app = request.app 320 root = app.root 321 if hasattr(root, '_cp_config'): 322 merge(root._cp_config) 323 if '/' in app.config: 324 merge(app.config['/']) 325 atoms = [ x for x in path_info.split('/') if x ] 326 if atoms: 327 last = atoms.pop() 328 else: 329 last = None 330 for atom in atoms: 331 curpath = '/'.join((curpath, atom)) 332 if curpath in app.config: 333 merge(app.config[curpath]) 334 335 handler = None 336 if result: 337 controller = result.get('controller') 338 controller = self.controllers.get(controller, controller) 339 if controller: 340 if isinstance(controller, (type, types.ClassType)): 341 controller = controller() 342 if hasattr(controller, '_cp_config'): 343 merge(controller._cp_config) 344 action = result.get('action') 345 if action is not None: 346 handler = getattr(controller, action, None) 347 if hasattr(handler, '_cp_config'): 348 merge(handler._cp_config) 349 else: 350 handler = controller 351 if last: 352 curpath = '/'.join((curpath, last)) 353 if curpath in app.config: 354 merge(app.config[curpath]) 355 return handler 356 357 358 def XMLRPCDispatcher(next_dispatcher = Dispatcher()): 359 from cherrypy.lib import xmlrpc 360 361 def xmlrpc_dispatch(path_info): 362 path_info = xmlrpc.patched_path(path_info) 363 return next_dispatcher(path_info) 364 365 return xmlrpc_dispatch 366 367 368 def VirtualHost(next_dispatcher = Dispatcher(), use_x_forwarded_host = True, **domains): 369 from cherrypy.lib import httputil 370 371 def vhost_dispatch(path_info): 372 request = cherrypy.serving.request 373 header = request.headers.get 374 domain = header('Host', '') 375 if use_x_forwarded_host: 376 domain = header('X-Forwarded-Host', domain) 377 prefix = domains.get(domain, '') 378 if prefix: 379 path_info = httputil.urljoin(prefix, path_info) 380 result = next_dispatcher(path_info) 381 section = request.config.get('tools.staticdir.section') 382 if section: 383 section = section[len(prefix):] 384 request.config['tools.staticdir.section'] = section 385 return result 386 387 return vhost_dispatch