NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

_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