Changeset 1134
- Timestamp:
- 06/11/06 22:57:21
- Files:
-
- trunk/cherrypy/__init__.py (modified) (5 diffs)
- trunk/cherrypy/_cperror.py (modified) (10 diffs)
- trunk/cherrypy/_cptools.py (moved) (moved from trunk/cherrypy/tools.py) (7 diffs)
- trunk/cherrypy/lib/cptools.py (modified) (1 diff)
- trunk/cherrypy/test/test_core.py (modified) (2 diffs)
- trunk/cherrypy/test/test_response_headers.py (modified) (1 diff)
- trunk/cherrypy/test/test_tools.py (modified) (4 diffs)
- trunk/cherrypy/test/test_xmlrpc.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/__init__.py
r1120 r1134 3 3 __version__ = '3.0.0alpha' 4 4 5 import cgi6 import datetime7 import sys8 import traceback9 import types10 5 11 6 from _cperror import * 12 7 import config 13 import tools 8 9 import _cptools 10 tools = _cptools.default_toolbox 14 11 15 12 import _cptree 16 13 tree = _cptree.Tree() 17 18 14 import _cpengine 19 15 engine = _cpengine.Engine() … … 21 17 server = _cpserver.Server() 22 18 19 def quickstart(root, script_name="", conf=None): 20 """Mount the given app, start the engine and builtin server, then block.""" 21 tree.mount(root, script_name, conf) 22 server.start() 23 engine.start() 24 23 25 codecoverage = False 24 26 25 27 try: 26 from threading import local 28 from threading import local as _local 27 29 except ImportError: 28 from cherrypy._cpthreadinglocal import local 30 from cherrypy._cpthreadinglocal import local as _local 29 31 30 32 # Create a threadlocal object to hold the request, response, and other … … 32 34 # a new HTTP conversation, yet still refer to them as module-level globals 33 35 # in a thread-safe way. 34 serving = local()36 serving = _local() 35 37 36 38 class _ThreadLocalProxy: … … 70 72 71 73 # Create thread_data object as a thread-specific all-purpose storage 72 thread_data = local() 73 74 def expose(func=None, alias=None): 75 """Expose the function, optionally providing an alias or set of aliases.""" 76 77 def expose_(func): 78 func.exposed = True 79 if alias is not None: 80 if isinstance(alias, basestring): 81 parents[alias.replace(".", "_")] = func 82 else: 83 for a in alias: 84 parents[a.replace(".", "_")] = func 85 return func 86 87 parents = sys._getframe(1).f_locals 88 if isinstance(func, (types.FunctionType, types.MethodType)): 89 # expose is being called directly, before the method has been bound 90 return expose_(func) 91 else: 92 # expose is being called as a decorator 93 if alias is None: 94 alias = func 95 return expose_ 96 97 def set_config(**kwargs): 98 """Decorator to set _cp_config using the given kwargs.""" 99 def wrapper(f): 100 if not hasattr(f, "_cp_config"): 101 f._cp_config = {} 102 f._cp_config.update(kwargs) 103 return f 104 return wrapper 74 thread_data = _local() 75 76 77 78 # Logging # 105 79 106 80 107 81 def logtime(): 82 import datetime 108 83 now = datetime.datetime.now() 109 84 from cherrypy.lib import httptools … … 172 147 173 148 174 def quickstart(root, script_name="", conf=None): 175 """Mount the given app, start the engine and builtin server, then block.""" 176 tree.mount(root, script_name, conf) 177 server.start() 178 engine.start() 179 149 150 # Helper functions for CP apps # 151 152 153 def decorate(func, decorator): 154 """ 155 Return the decorated func. This will automatically copy all 156 non-standard attributes (like exposed) to the newly decorated function. 157 """ 158 import inspect 159 newfunc = decorator(func) 160 for (k,v) in inspect.getmembers(func): 161 if not hasattr(newfunc, k): 162 setattr(newfunc, k, v) 163 return newfunc 164 165 def decorateAll(obj, decorator): 166 """ 167 Recursively decorate all exposed functions of obj and all of its children, 168 grandchildren, etc. If you used to use aspects, you might want to look 169 into these. This function modifies obj; there is no return value. 170 """ 171 import inspect 172 obj_type = type(obj) 173 for (k,v) in inspect.getmembers(obj): 174 if hasattr(obj_type, k): # only deal with user-defined attributes 175 continue 176 if callable(v) and getattr(v, "exposed", False): 177 setattr(obj, k, decorate(v, decorator)) 178 decorateAll(v, decorator) 179 180 181 class ExposeItems: 182 """ 183 Utility class that exposes a getitem-aware object. It does not provide 184 index() or default() methods, and it does not expose the individual item 185 objects - just the list or dict that contains them. User-specific index() 186 and default() methods can be implemented by inheriting from this class. 187 188 Use case: 189 190 from cherrypy import ExposeItems 191 ... 192 root.foo = ExposeItems(mylist) 193 root.bar = ExposeItems(mydict) 194 """ 195 exposed = True 196 def __init__(self, items): 197 self.items = items 198 def __getattr__(self, key): 199 return self.items[key] 200 201 202 def expose(func=None, alias=None): 203 """Expose the function, optionally providing an alias or set of aliases.""" 204 205 def expose_(func): 206 func.exposed = True 207 if alias is not None: 208 if isinstance(alias, basestring): 209 parents[alias.replace(".", "_")] = func 210 else: 211 for a in alias: 212 parents[a.replace(".", "_")] = func 213 return func 214 215 import sys, types 216 parents = sys._getframe(1).f_locals 217 if isinstance(func, (types.FunctionType, types.MethodType)): 218 # expose is being called directly, before the method has been bound 219 return expose_(func) 220 else: 221 # expose is being called as a decorator 222 if alias is None: 223 alias = func 224 return expose_ 225 226 def set_config(**kwargs): 227 """Decorator to set _cp_config using the given kwargs.""" 228 def wrapper(f): 229 if not hasattr(f, "_cp_config"): 230 f._cp_config = {} 231 f._cp_config.update(kwargs) 232 return f 233 return wrapper 234 trunk/cherrypy/_cperror.py
r1130 r1134 1 1 """Error classes for CherryPy.""" 2 2 3 import cgi 4 import sys 5 import traceback 6 import urlparse 7 8 from cherrypy.lib import httptools 3 # cherrypy imports this module as *, so hide nonessentials. 4 from cgi import escape as _escape 5 from sys import exc_info as _exc_info 6 from urlparse import urljoin as _urljoin 7 from cherrypy.lib import httptools as _httptools 9 8 10 9 … … 32 31 path, pm = path.split("?", 1) 33 32 request.query_string = pm 34 request.params = httptools.parseQueryString(pm)33 request.params = _httptools.parseQueryString(pm) 35 34 36 35 # Note that urljoin will "do the right thing" whether url is: … … 38 37 # 2. a URL relative to the current path 39 38 # Note that any querystring will be discarded. 40 path = urlparse.urljoin(cherrypy.request.path_info, path)39 path = _urljoin(cherrypy.request.path_info, path) 41 40 42 41 # Set a 'path' member attribute so that code which traps this … … 70 69 # 3. a URL relative to the current path 71 70 # Note that any querystring in browser_url will be discarded. 72 url = urlparse.urljoin(cherrypy.request.browser_url, url)71 url = _urljoin(cherrypy.request.browser_url, url) 73 72 abs_urls.append(url) 74 73 self.urls = abs_urls … … 179 178 response.headers['Content-Type'] = "text/html" 180 179 181 be_ie_unfriendly(self.status)180 _be_ie_unfriendly(self.status) 182 181 183 182 … … 230 229 231 230 try: 232 code, reason, message = httptools.validStatus(status)231 code, reason, message = _httptools.validStatus(status) 233 232 except ValueError, x: 234 233 raise cherrypy.HTTPError(500, x.args[0]) … … 248 247 kwargs[k] = "" 249 248 else: 250 kwargs[k] = cgi.escape(kwargs[k])249 kwargs[k] = _escape(kwargs[k]) 251 250 252 251 template = _HTTPErrorTemplate … … 260 259 m += "<br />" 261 260 m += ("In addition, the custom error page " 262 "failed:\n<br />%s" % ( sys.exc_info()[1]))261 "failed:\n<br />%s" % (_exc_info()[1])) 263 262 kwargs['message'] = m 264 263 … … 273 272 274 273 275 def be_ie_unfriendly(status):274 def _be_ie_unfriendly(status): 276 275 import cherrypy 277 276 response = cherrypy.response … … 300 299 """format_exc(exc=None) -> exc (or sys.exc_info if None), formatted.""" 301 300 if exc is None: 302 exc = sys.exc_info()301 exc = _exc_info() 303 302 if exc == (None, None, None): 304 303 return "" 304 import traceback 305 305 return "".join(traceback.format_exception(*exc)) 306 306 trunk/cherrypy/_cptools.py
r1129 r1134 34 34 self.point = point 35 35 self.callable = callable 36 if name is None:37 name = callable.__name__38 36 self.name = name 39 37 # TODO: add an attribute to self for each arg … … 172 170 173 171 174 175 172 # Builtin tools # 176 173 177 from cherrypy.lib import cptools 178 session_auth = MainTool(cptools.session_auth) 179 base_url = Tool('before_request_body', cptools.base_url) 180 response_headers = Tool('before_finalize', cptools.response_headers) 181 # We can't call virtual_host in on_start_resource, 182 # because it's failsafe and the redirect would be swallowed. 183 virtual_host = Tool('before_request_body', cptools.virtual_host) 184 log_tracebacks = Tool('before_error_response', cptools.log_traceback, 'log_tracebacks') 185 log_headers = Tool('before_error_response', cptools.log_request_headers, 'log_headers') 186 err_redirect = ErrorTool(cptools.redirect, 'err_redirect') 187 etags = Tool('before_finalize', cptools.validate_etags, 'etags') 188 del cptools 189 190 from cherrypy.lib import encodings 191 decode = Tool('before_main', encodings.decode) 192 encode = Tool('before_finalize', encodings.encode) 193 gzip = Tool('before_finalize', encodings.gzip) 194 del encodings 195 196 from cherrypy.lib import static 197 class _StaticDirTool(MainTool): 174 from cherrypy.lib import cptools, encodings, static 175 from cherrypy.lib import sessions as _sessions, xmlrpc as _xmlrpc 176 from cherrypy.lib import caching as _caching, wsgiapp as _wsgiapp 177 178 179 class StaticDirTool(MainTool): 198 180 def setup(self): 199 181 """Hook this tool into cherrypy.request using the given conf.""" … … 204 186 # Don't pass conf (or our wrapper will get wrapped!) 205 187 cherrypy.request.hooks.attach(self.point, wrapper) 206 staticdir = _StaticDirTool(static.staticdir) 207 staticfile = MainTool(static.staticfile) 208 del static 209 210 from cherrypy.lib import sessions as _sessions 211 class _SessionTool(Tool): 188 189 190 class SessionTool(Tool): 212 191 def __init__(self): 213 192 self.point = "before_finalize" … … 256 235 cherrypy.request.hooks.attach('before_finalize', _sessions.save) 257 236 cherrypy.request.hooks.attach('on_end_request', _sessions.cleanup) 258 sessions = _SessionTool() 259 260 from cherrypy.lib import xmlrpc as _xmlrpc 237 238 261 239 class XMLRPCController(object): 262 240 … … 284 262 index = __call__ 285 263 286 class _XMLRPCTool(object): 264 265 class XMLRPCTool(object): 287 266 """Tool for using XMLRPC over HTTP. 288 267 … … 302 281 if ppath != path_info: 303 282 raise cherrypy.InternalRedirect(ppath) 304 xmlrpc = _XMLRPCTool() 305 306 from cherrypy.lib import wsgiapp as _wsgiapp 307 class _WSGIAppTool(MainTool): 283 284 285 class WSGIAppTool(MainTool): 308 286 """A tool for running any WSGI middleware/application within CP. 309 287 … … 327 305 cherrypy.request.process_request_body = False 328 306 MainTool.setup(self) 329 wsgiapp = _WSGIAppTool(_wsgiapp.run, "wsgiapp") 330 del _wsgiapp 331 332 333 # These modules are themselves Tools 334 from cherrypy.lib import caching 307 308 309 class Toolbox(object): 310 """A collection of Tools.""" 311 312 def __setattr__(self, name, value): 313 # If the Tool.name is None, supply it from the attribute name. 314 if isinstance(value, Tool): 315 if value.name is None: 316 value.name = name 317 object.__setattr__(self, name, value) 318 319 320 default_toolbox = Toolbox() 321 default_toolbox.session_auth = MainTool(cptools.session_auth) 322 default_toolbox.base_url = Tool('before_request_body', cptools.base_url) 323 default_toolbox.response_headers = Tool('before_finalize', cptools.response_headers) 324 # We can't call virtual_host in on_start_resource, 325 # because it's failsafe and the redirect would be swallowed. 326 default_toolbox.virtual_host = Tool('before_request_body', cptools.virtual_host) 327 default_toolbox.log_tracebacks = Tool('before_error_response', cptools.log_traceback) 328 default_toolbox.log_headers = Tool('before_error_response', cptools.log_request_headers) 329 default_toolbox.err_redirect = ErrorTool(cptools.redirect) 330 default_toolbox.etags = Tool('before_finalize', cptools.validate_etags) 331 default_toolbox.decode = Tool('before_main', encodings.decode) 332 default_toolbox.encode = Tool('before_finalize', encodings.encode) 333 default_toolbox.gzip = Tool('before_finalize', encodings.gzip) 334 default_toolbox.staticdir = StaticDirTool(static.staticdir) 335 default_toolbox.staticfile = MainTool(static.staticfile) 336 default_toolbox.sessions = SessionTool() 337 default_toolbox.xmlrpc = XMLRPCTool() 338 default_toolbox.wsgiapp = WSGIAppTool(_wsgiapp.run) 339 default_toolbox.caching = _caching 340 341 342 del cptools, encodings, static trunk/cherrypy/lib/cptools.py
r1118 r1134 1 """Tools which both CherryPy and application developers may invoke.""" 2 3 import inspect 1 """Tools which CherryPy may invoke.""" 2 4 3 import md5 5 import os6 4 import sys 7 import time8 5 9 6 import cherrypy 10 7 import httptools 11 12 13 def decorate(func, decorator):14 """15 Return the decorated func. This will automatically copy all16 non-standard attributes (like exposed) to the newly decorated function.17 """18 newfunc = decorator(func)19 for (k,v) in inspect.getmembers(func):20 if not hasattr(newfunc, k):21 setattr(newfunc, k, v)22 return newfunc23 24 def decorateAll(obj, decorator):25 """26 Recursively decorate all exposed functions of obj and all of its children,27 grandchildren, etc. If you used to use aspects, you might want to look28 into these. This function modifies obj; there is no return value.29 """30 obj_type = type(obj)31 for (k,v) in inspect.getmembers(obj):32 if hasattr(obj_type, k): # only deal with user-defined attributes33 continue34 if callable(v) and getattr(v, "exposed", False):35 setattr(obj, k, decorate(v, decorator))36 decorateAll(v, decorator)37 38 39 class ExposeItems:40 """41 Utility class that exposes a getitem-aware object. It does not provide42 index() or default() methods, and it does not expose the individual item43 objects - just the list or dict that contains them. User-specific index()44 and default() methods can be implemented by inheriting from this class.45 46 Use case:47 48 from cherrypy.lib.cptools import ExposeItems49 ...50 root.foo = ExposeItems(mylist)51 root.bar = ExposeItems(mydict)52 """53 exposed = True54 def __init__(self, items):55 self.items = items56 def __getattr__(self, key):57 return self.items[key]58 8 59 9 trunk/cherrypy/test/test_core.py
r1131 r1134 5 5 6 6 import cherrypy 7 from cherrypy import tools7 from cherrypy import _cptools, tools 8 8 from cherrypy.lib import httptools, static 9 9 import types … … 138 138 if not getattr(cherrypy.request, "login", None): 139 139 raise cherrypy.InternalRedirect("/internalredirect/login") 140 tools.login_redir = tools.Tool('before_main', login_redir)140 tools.login_redir = _cptools.Tool('before_main', login_redir) 141 141 142 142 class InternalRedirect(Test): trunk/cherrypy/test/test_response_headers.py
r1114 r1134 3 3 4 4 import cherrypy 5 from cherrypy .tools import response_headers6 headers = response_headers.wrap5 from cherrypy import tools 6 headers = tools.response_headers.wrap 7 7 8 8 trunk/cherrypy/test/test_tools.py
r1115 r1134 7 7 8 8 import cherrypy 9 from cherrypy import tools9 from cherrypy import _cptools, tools 10 10 11 11 … … 17 17 if not getattr(cherrypy.request, "login", None): 18 18 raise cherrypy.HTTPError(401) 19 tools.check_access = tools.Tool('before_request_body', check_access)19 tools.check_access = _cptools.Tool('before_request_body', check_access) 20 20 21 21 def numerify(): … … 27 27 cherrypy.response.body = number_it(cherrypy.response.body) 28 28 29 class NumTool( tools.Tool):29 class NumTool(_cptools.Tool): 30 30 def setup(self): 31 31 def makemap(): … … 36 36 tools.numerify = NumTool('before_finalize', numerify) 37 37 38 # It's not mandatory to inherit from tools.Tool.38 # It's not mandatory to inherit from _cptools.Tool. 39 39 class NadsatTool: 40 40 trunk/cherrypy/test/test_xmlrpc.py
r1114 r1134 6 6 def setup_server(): 7 7 import cherrypy 8 from cherrypy import tools8 from cherrypy import _cptools 9 9 10 10 class Root: … … 14 14 15 15 16 class XmlRpc( tools.XMLRPCController):16 class XmlRpc(_cptools.XMLRPCController): 17 17 18 18 def return_single_item_list(self):

