"""CherryPy Application and Tree objects."""importosimportcherrypyfromcherrypyimport_cpconfig,_cplogging,_cpwsgi,toolsclassApplication(object):"""A CherryPy Application. An instance of this class may also be used as a WSGI callable (WSGI application object) for itself. root: the top-most container of page handlers for this app. script_name: the URL "mount point" for this app; for example, if script_name is "/my/cool/app", then the URL "http://my.domain.tld/my/cool/app/page1" might be handled by a "page1" method on the root object. If script_name is explicitly set to None, then CherryPy will attempt to provide it each time from request.wsgi_environ['SCRIPT_NAME']. config: a dict of {path: pathconf} pairs, where 'pathconf' is itself a dict of {key: value} pairs. """def__init__(self,root,script_name=""):self.log=_cplogging.LogManager(id(self))self.root=rootself.script_name=script_nameself.wsgiapp=_cpwsgi.CPWSGIApp(self)self.namespaces={"log":lambdak,v:setattr(self.log,k,v),"wsgi":self.wsgiapp.namespace_handler,}self.config={}def_get_script_name(self):ifself._script_nameisNone:# None signals that the script name should be pulled from WSGI environ.returncherrypy.request.wsgi_environ['SCRIPT_NAME']returnself._script_namedef_set_script_name(self,value):self._script_name=valuescript_name=property(fget=_get_script_name,fset=_set_script_name)defmerge(self,config):"""Merge the given config into self.config."""_cpconfig.merge(self.config,config)# Handle namespaces specified in config._cpconfig._call_namespaces(self.config.get("/",{}),self.namespaces)def__call__(self,environ,start_response):returnself.wsgiapp(environ,start_response)classTree(object):"""A registry of CherryPy applications, mounted at diverse points. An instance of this class may also be used as a WSGI callable (WSGI application object), in which case it dispatches to all mounted apps. apps: a dict of the form {script name: application}, where "script name" is a string declaring the URL mount point (no trailing slash), and "application" is an instance of cherrypy.Application (or an arbitrary WSGI callable if you happen to be using a WSGI server). """def__init__(self):self.apps={}defmount(self,root,script_name="",config=None):"""Mount a new app from a root object, script_name, and config."""# Next line both 1) strips trailing slash and 2) maps "/" -> "".script_name=script_name.rstrip("/")ifisinstance(root,Application):app=rootelse:app=Application(root,script_name)# If mounted at "", add favicon.icoifscript_name==""androotandnothasattr(root,"favicon_ico"):favicon=os.path.join(os.getcwd(),os.path.dirname(__file__),"favicon.ico")root.favicon_ico=tools.staticfile.handler(favicon)ifconfig:app.merge(config)self.apps[script_name]=appreturnappdefgraft(self,wsgi_callable,script_name=""):"""Mount a wsgi callable at the given script_name."""# Next line both 1) strips trailing slash and 2) maps "/" -> "".script_name=script_name.rstrip("/")self.apps[script_name]=wsgi_callabledefscript_name(self,path=None):"""The script_name of the app at the given path, or None. If path is None, cherrypy.request is used. """ifpathisNone:try:path=cherrypy.request.script_name+cherrypy.request.path_infoexceptAttributeError:returnNonewhileTrue:ifpathinself.apps:returnpathifpath=="":returnNone# Move one node up the tree and try again.path=path[:path.rfind("/")]def__call__(self,environ,start_response):# If you're calling this, then you're probably setting SCRIPT_NAME# to '' (some WSGI servers always set SCRIPT_NAME to '').# Try to look up the app using the full path.path=environ.get('SCRIPT_NAME','')+environ.get('PATH_INFO','')sn=self.script_name(pathor"/")ifsnisNone:start_response('404 Not Found',[])return[]app=self.apps[sn]# Correct the SCRIPT_NAME and PATH_INFO environ entries.environ=environ.copy()environ['SCRIPT_NAME']=snenviron['PATH_INFO']=path[len(sn.rstrip("/")):]returnapp(environ,start_response)