source file: /Library/Python/2.3/site-packages/CherryPy-3.0.1-py2.3.egg/cherrypy/_cptree.py
file stats: 82 lines, 54 executed: 65.9% covered
1. """CherryPy Application and Tree objects.""" 2. 3. import os 4. import cherrypy 5. from cherrypy import _cpconfig, _cplogging, _cpwsgi, tools 6. 7. 8. class Application(object): 9. """A CherryPy Application. 10. 11. An instance of this class may also be used as a WSGI callable 12. (WSGI application object) for itself. 13. """ 14. 15. __metaclass__ = cherrypy._AttributeDocstrings 16. 17. root = None 18. root__doc = """ 19. The top-most container of page handlers for this app. Handlers should 20. be arranged in a hierarchy of attributes, matching the expected URI 21. hierarchy; the default dispatcher then searches this hierarchy for a 22. matching handler. When using a dispatcher other than the default, 23. this value may be None.""" 24. 25. config = {} 26. config__doc = """ 27. A dict of {path: pathconf} pairs, where 'pathconf' is itself a dict 28. of {key: value} pairs.""" 29. 30. namespaces = _cpconfig.NamespaceSet() 31. 32. log = None 33. log__doc = """A LogManager instance. See _cplogging.""" 34. 35. wsgiapp = None 36. wsgiapp__doc = """A CPWSGIApp instance. See _cpwsgi.""" 37. 38. def __init__(self, root, script_name=""): 39. self.log = _cplogging.LogManager(id(self), cherrypy.log.logger_root) 40. self.root = root 41. self.script_name = script_name 42. self.wsgiapp = _cpwsgi.CPWSGIApp(self) 43. 44. self.namespaces = self.namespaces.copy() 45. self.namespaces["log"] = lambda k, v: setattr(self.log, k, v) 46. self.namespaces["wsgi"] = self.wsgiapp.namespace_handler 47. 48. self.config = {} 49. 50. script_name__doc = """ 51. The URI "mount point" for this app; for example, if script_name is 52. "/my/cool/app", then the URL "http://my.domain.tld/my/cool/app/page1" 53. might be handled by a "page1" method on the root object. If script_name 54. is explicitly set to None, then the script_name will be provided 55. for each call from request.wsgi_environ['SCRIPT_NAME'].""" 56. def _get_script_name(self): 57. if self._script_name is None: 58. # None signals that the script name should be pulled from WSGI environ. 59. return cherrypy.request.wsgi_environ['SCRIPT_NAME'] 60. return self._script_name 61. def _set_script_name(self, value): 62. self._script_name = value 63. script_name = property(fget=_get_script_name, fset=_set_script_name, 64. doc=script_name__doc) 65. 66. def merge(self, config): 67. """Merge the given config into self.config.""" 68. _cpconfig.merge(self.config, config) 69. 70. # Handle namespaces specified in config. 71. self.namespaces(self.config.get("/", {})) 72. 73. def __call__(self, environ, start_response): 74. return self.wsgiapp(environ, start_response) 75. 76. 77. class Tree(object): 78. """A registry of CherryPy applications, mounted at diverse points. 79. 80. An instance of this class may also be used as a WSGI callable 81. (WSGI application object), in which case it dispatches to all 82. mounted apps. 83. """ 84. 85. apps = {} 86. apps__doc = """ 87. A dict of the form {script name: application}, where "script name" 88. is a string declaring the URI mount point (no trailing slash), and 89. "application" is an instance of cherrypy.Application (or an arbitrary 90. WSGI callable if you happen to be using a WSGI server).""" 91. 92. def __init__(self): 93. self.apps = {} 94. 95. def mount(self, root, script_name="", config=None): 96. """Mount a new app from a root object, script_name, and config.""" 97. # Next line both 1) strips trailing slash and 2) maps "/" -> "". 98. script_name = script_name.rstrip("/") 99. 100. if isinstance(root, Application): 101. app = root 102. else: 103. app = Application(root, script_name) 104. 105. # If mounted at "", add favicon.ico 106. if script_name == "" and root and not hasattr(root, "favicon_ico"): 107. favicon = os.path.join(os.getcwd(), os.path.dirname(__file__), 108. "favicon.ico") 109. root.favicon_ico = tools.staticfile.handler(favicon) 110. 111. if config: 112. app.merge(config) 113. 114. self.apps[script_name] = app 115. 116. return app 117. 118. def graft(self, wsgi_callable, script_name=""): 119. """Mount a wsgi callable at the given script_name.""" 120. # Next line both 1) strips trailing slash and 2) maps "/" -> "". 121. script_name = script_name.rstrip("/") 122. self.apps[script_name] = wsgi_callable 123. 124. def script_name(self, path=None): 125. """The script_name of the app at the given path, or None. 126. 127. If path is None, cherrypy.request is used. 128. """ 129. 130. if path is None: 131. try: 132. path = cherrypy.request.script_name + cherrypy.request.path_info 133. except AttributeError: 134. return None 135. 136. while True: 137. if path in self.apps: 138. return path 139. 140. if path == "": 141. return None 142. 143. # Move one node up the tree and try again. 144. path = path[:path.rfind("/")] 145. 146. def __call__(self, environ, start_response): 147. # If you're calling this, then you're probably setting SCRIPT_NAME 148. # to '' (some WSGI servers always set SCRIPT_NAME to ''). 149. # Try to look up the app using the full path. 150. path = environ.get('SCRIPT_NAME', '') + environ.get('PATH_INFO', '') 151. sn = self.script_name(path or "/") 152. if sn is None: 153. start_response('404 Not Found', []) 154. return [] 155. 156. app = self.apps[sn] 157. 158. # Correct the SCRIPT_NAME and PATH_INFO environ entries. 159. environ = environ.copy() 160. environ['SCRIPT_NAME'] = sn 161. environ['PATH_INFO'] = path[len(sn.rstrip("/")):] 162. return app(environ, start_response) 163.