diff --git a/pecan/core.py b/pecan/core.py index d892706..47ba627 100644 --- a/pecan/core.py +++ b/pecan/core.py @@ -24,8 +24,6 @@ def proxy(key): class ObjectProxy(object): def __getattr__(self, attr): obj = getattr(state, key) - if attr == 'validation_errors': - return getattr(obj, attr, {}) return getattr(obj, attr) def __setattr__(self, attr, value): obj = getattr(state, key) @@ -41,9 +39,9 @@ response = proxy('response') def override_template(template, content_type=None): - request.override_template = template + request.pecan['override_template'] = template if content_type: - request.override_content_type = content_type + request.pecan['override_content_type'] = content_type def abort(status_code=None, detail='', headers=None, comment=None): raise exc.status_map[status_code](detail=detail, headers=headers, comment=comment) @@ -60,11 +58,9 @@ def redirect(location, internal=False, code=None, headers={}): def error_for(field): - if not request.validation_errors: - return '' - return request.validation_errors.get(field, '') + return request.pecan['validation_errors'].get(field, '') + - def static(name, value): if 'pecan.params' not in request.environ: request.environ['pecan.params'] = dict(request.str_params) @@ -95,10 +91,10 @@ class ValidationException(ForwardRequestException): location = cfg['error_handler'] if callable(location): location = location() - merge_dicts(request.validation_errors, errors) + merge_dicts(request.pecan['validation_errors'], errors) if 'pecan.params' not in request.environ: request.environ['pecan.params'] = dict(request.str_params) - request.environ['pecan.validation_errors'] = request.validation_errors + request.environ['pecan.validation_errors'] = request.pecan['validation_errors'] if cfg.get('htmlfill') is not None: request.environ['pecan.htmlfill'] = cfg['htmlfill'] request.environ['REQUEST_METHOD'] = 'GET' @@ -138,7 +134,11 @@ class Pecan(object): except NonCanonicalPath, e: if self.force_canonical and not _cfg(e.controller).get('accept_noncanonical', False): if request.method == 'POST': - raise RuntimeError, "You have POSTed to a URL '%s' which requires a slash. Most browsers will not maintain POST data when redirected. Please update your code to POST to '%s/' or set force_canonical to False" % (request.routing_path, request.routing_path) + raise RuntimeError, "You have POSTed to a URL '%s' which '\ + 'requires a slash. Most browsers will not maintain '\ + 'POST data when redirected. Please update your code '\ + 'to POST to '%s/' or set force_canonical to False" % \ + (request.pecan['routing_path'], request.pecan['routing_path']) raise exc.HTTPFound(add_slash=True) return e.controller, e.remainder @@ -171,9 +171,9 @@ class Pecan(object): args.append(im_self) # grab the routing args from nested REST controllers - if hasattr(request, 'routing_args'): - remainder = request.routing_args + list(remainder) - delattr(request, 'routing_args') + if 'routing_args' in request.pecan: + remainder = request.pecan['routing_args'] + list(remainder) + del request.pecan['routing_args'] # handle positional arguments if valid_args and remainder: @@ -212,7 +212,6 @@ class Pecan(object): def validate(self, schema, params, json=False, error_handler=None, htmlfill=None, variable_decode=None): - request.validation_errors = {} try: to_validate = params if json: @@ -225,7 +224,7 @@ class Pecan(object): if variable_decode is not None: kwargs['encode_variables'] = True kwargs.update(variable_decode) - request.validation_errors = e.unpack_errors(**kwargs) + request.pecan['validation_errors'] = e.unpack_errors(**kwargs) if error_handler is not None: raise ValidationException() if json: @@ -238,20 +237,20 @@ class Pecan(object): state.hooks = self.determine_hooks() # store the routing path to allow hooks to modify it - request.routing_path = request.path + request.pecan['routing_path'] = request.path # handle "on_route" hooks self.handle_hooks('on_route', state) # lookup the controller, respecting content-type as requested # by the file extension on the URI - path = request.routing_path + path = request.pecan['routing_path'] - if state.content_type is None and '.' in path.split('/')[-1]: + if not request.pecan['content_type'] and '.' in path.split('/')[-1]: path, format = os.path.splitext(path) # store the extension for retrieval by controllers - request.extension = format - state.content_type = self.get_content_type(format) + request.pecan['extension'] = format + request.pecan['content_type'] = self.get_content_type(format) controller, remainder = self.route(self.root, path) cfg = _cfg(controller) @@ -270,8 +269,8 @@ class Pecan(object): state.controller = controller # if unsure ask the controller for the default content type - if state.content_type is None: - state.content_type = cfg.get('content_type', 'text/html') + if not request.pecan['content_type']: + request.pecan['content_type'] = cfg.get('content_type', 'text/html') # get a sorted list of hooks, by priority state.hooks = self.determine_hooks(controller) @@ -291,7 +290,7 @@ class Pecan(object): variable_decode=cfg.get('variable_decode') ) elif 'pecan.validation_errors' in request.environ: - request.validation_errors = request.environ.pop('pecan.validation_errors') + request.pecan['validation_errors'] = request.environ.pop('pecan.validation_errors') # fetch the arguments for the controller args, kwargs = self.get_args( @@ -312,16 +311,16 @@ class Pecan(object): raw_namespace = result # pull the template out based upon content type and handle overrides - template = cfg.get('content_types', {}).get(state.content_type) + template = cfg.get('content_types', {}).get(request.pecan['content_type']) # check if for controller override of template - template = getattr(request, 'override_template', template) - state.content_type = getattr(request, 'override_content_type', state.content_type) + template = request.pecan.get('override_template', template) + request.pecan['content_type'] = request.pecan.get('override_content_type', request.pecan['content_type']) # if there is a template, render it if template: if template == 'json': - state.content_type = self.get_content_type('.json') + request.pecan['content_type'] = self.get_content_type('.json') result = render(template, result) # pass the response through htmlfill (items are popped out of the @@ -331,8 +330,8 @@ class Pecan(object): _htmlfill = request.environ.pop('pecan.htmlfill') if 'pecan.params' in request.environ: params = request.environ.pop('pecan.params') - if request.validation_errors and _htmlfill is not None and state.content_type == 'text/html': - errors = getattr(request, 'validation_errors', {}) + if request.pecan['validation_errors'] and _htmlfill is not None and request.pecan['content_type'] == 'text/html': + errors = request.pecan['validation_errors'] result = htmlfill.render(result, defaults=params, errors=errors, **_htmlfill) # If we are in a test request put the namespace where it can be @@ -350,21 +349,21 @@ class Pecan(object): response.body = result # set the content type - if state.content_type: - response.content_type = state.content_type + if request.pecan['content_type']: + response.content_type = request.pecan['content_type'] def __call__(self, environ, start_response): # create the request and response object state.request = Request(environ) - state.content_type = None state.response = Response() state.hooks = [] state.app = self # handle the request try: - # add context to the request + # add context and environment to the request state.request.context = {} + state.request.pecan = dict(content_type=None, validation_errors={}) self.handle_request() except Exception, e: @@ -387,7 +386,6 @@ class Pecan(object): return state.response(environ, start_response) finally: # clean up state - del state.content_type del state.hooks del state.request del state.response diff --git a/pecan/rest.py b/pecan/rest.py index 88eed0a..ef87bfb 100644 --- a/pecan/rest.py +++ b/pecan/rest.py @@ -52,7 +52,7 @@ class RestController(object): # get the args to figure out how much to chop off args = getargspec(getattr(self, method)) - fixed_args = len(args[0][1:]) - len(getattr(request, 'routing_args', [])) + fixed_args = len(args[0][1:]) - len(request.pecan.get('routing_args', [])) var_args = args[1] # attempt to locate a sub-controller @@ -161,7 +161,4 @@ class RestController(object): _handle_put = _handle_post def _set_routing_args(self, args): - if hasattr(request, 'routing_args'): - request.routing_args.extend(args) - else: - setattr(request, 'routing_args', args) + request.pecan.setdefault('routing_args', []).extend(args) diff --git a/tests/test_base.py b/tests/test_base.py index 183b20d..6a8fac2 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -514,7 +514,7 @@ class TestBase(TestCase): @expose() def _default(self, *args): from pecan.core import request - return request.extension + return request.pecan['extension'] app = TestApp(Pecan(RootController())) r = app.get('/index.html') diff --git a/tests/test_hooks.py b/tests/test_hooks.py index 2261983..fb1cb5f 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py @@ -696,7 +696,7 @@ class TestHooks(object): password_confirm, age): run_hook.append('inside') - return str(len(request.validation_errors) > 0) + return str(len(request.pecan['validation_errors']) > 0) @expose(schema=RegistrationSchema(), error_handler='/errors') def with_handler(self, first_name, @@ -707,7 +707,7 @@ class TestHooks(object): password_confirm, age): run_hook.append('inside') - return str(len(request.validation_errors) > 0) + return str(len(request.pecan['validation_errors']) > 0) # test that the hooks get properly run with no validation errors app = TestApp(make_app(RootController(), hooks=[SimpleHook()])) diff --git a/tests/test_validation.py b/tests/test_validation.py index 9651450..cafbf47 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -95,7 +95,7 @@ class TestValidation(object): @expose() def errors(self, *args, **kwargs): - assert len(request.validation_errors) > 0 + assert len(request.pecan['validation_errors']) > 0 return 'There was an error!' @expose(schema=RegistrationSchema()) @@ -106,7 +106,7 @@ class TestValidation(object): password, password_confirm, age): - assert len(request.validation_errors) > 0 + assert len(request.pecan['validation_errors']) > 0 return 'Success!' @expose(schema=RegistrationSchema(), error_handler='/errors') @@ -117,17 +117,17 @@ class TestValidation(object): password, password_confirm, age): - assert len(request.validation_errors) > 0 + assert len(request.pecan['validation_errors']) > 0 return 'Success!' @expose(json_schema=RegistrationSchema()) def json(self, data): - assert len(request.validation_errors) > 0 + assert len(request.pecan['validation_errors']) > 0 return 'Success!' @expose(json_schema=RegistrationSchema(), error_handler='/errors') def json_with_handler(self, data): - assert len(request.validation_errors) > 0 + assert len(request.pecan['validation_errors']) > 0 return 'Success!' @@ -193,13 +193,13 @@ class TestValidation(object): @expose() def errors(self, *args, **kwargs): - return 'Error with %s!' % ', '.join(request.validation_errors.keys()) + return 'Error with %s!' % ', '.join(request.pecan['validation_errors'].keys()) @expose(schema=ColorSchema(), variable_decode=True) def index(self, **kwargs): - if request.validation_errors: - return ', '.join(request.validation_errors.keys()) + if request.pecan['validation_errors']: + return ', '.join(request.pecan['validation_errors'].keys()) else: return 'Success!' @@ -207,16 +207,16 @@ class TestValidation(object): error_handler='/errors', variable_decode=True) def with_handler(self, **kwargs): - if request.validation_errors: - return ', '.join(request.validation_errors.keys()) + if request.pecan['validation_errors']: + return ', '.join(request.pecan['validation_errors'].keys()) else: return 'Success!' @expose(json_schema=ColorSchema(), variable_decode=True) def json(self, data): - if request.validation_errors: - return ', '.join(request.validation_errors.keys()) + if request.pecan['validation_errors']: + return ', '.join(request.pecan['validation_errors'].keys()) else: return 'Success!' @@ -224,16 +224,16 @@ class TestValidation(object): error_handler='/errors', variable_decode=True) def json_with_handler(self, data): - if request.validation_errors: - return ', '.join(request.validation_errors.keys()) + if request.pecan['validation_errors']: + return ', '.join(request.pecan['validation_errors'].keys()) else: return 'Success!' @expose(schema=ColorSchema(), variable_decode=dict()) def custom(self, **kwargs): - if request.validation_errors: - return ', '.join(request.validation_errors.keys()) + if request.pecan['validation_errors']: + return ', '.join(request.pecan['validation_errors'].keys()) else: return 'Success!' @@ -241,16 +241,16 @@ class TestValidation(object): error_handler='/errors', variable_decode=dict()) def custom_with_handler(self, **kwargs): - if request.validation_errors: - return ', '.join(request.validation_errors.keys()) + if request.pecan['validation_errors']: + return ', '.join(request.pecan['validation_errors'].keys()) else: return 'Success!' @expose(json_schema=ColorSchema(), variable_decode=dict()) def custom_json(self, data): - if request.validation_errors: - return ', '.join(request.validation_errors.keys()) + if request.pecan['validation_errors']: + return ', '.join(request.pecan['validation_errors'].keys()) else: return 'Success!' @@ -258,16 +258,16 @@ class TestValidation(object): error_handler='/errors', variable_decode=dict()) def custom_json_with_handler(self, data): - if request.validation_errors: - return ', '.join(request.validation_errors.keys()) + if request.pecan['validation_errors']: + return ', '.join(request.pecan['validation_errors'].keys()) else: return 'Success!' @expose(schema=ColorSchema(), variable_decode=dict(dict_char='-', list_char='.')) def alternate(self, **kwargs): - if request.validation_errors: - return ', '.join(request.validation_errors.keys()) + if request.pecan['validation_errors']: + return ', '.join(request.pecan['validation_errors'].keys()) else: return 'Success!' @@ -275,16 +275,16 @@ class TestValidation(object): error_handler='/errors', variable_decode=dict(dict_char='-', list_char='.')) def alternate_with_handler(self, **kwargs): - if request.validation_errors: - return ', '.join(request.validation_errors.keys()) + if request.pecan['validation_errors']: + return ', '.join(request.pecan['validation_errors'].keys()) else: return 'Success!' @expose(json_schema=ColorSchema(), variable_decode=dict(dict_char='-', list_char='.')) def alternate_json(self, data): - if request.validation_errors: - return ', '.join(request.validation_errors.keys()) + if request.pecan['validation_errors']: + return ', '.join(request.pecan['validation_errors'].keys()) else: return 'Success!' @@ -292,8 +292,8 @@ class TestValidation(object): error_handler='/errors', variable_decode=dict(dict_char='-', list_char='.')) def alternate_json_with_handler(self, data): - if request.validation_errors: - return ', '.join(request.validation_errors.keys()) + if request.pecan['validation_errors']: + return ', '.join(request.pecan['validation_errors'].keys()) else: return 'Success!' @@ -507,7 +507,7 @@ class TestValidation(object): schema=ColorSchema(), variable_decode=True) def index(self, **kwargs): - if request.validation_errors: + if request.pecan['validation_errors']: return dict() else: return dict(data=kwargs) @@ -542,8 +542,8 @@ class TestValidation(object): schema=NameSchema(), htmlfill=dict(auto_insert_errors=True)) def json(self, **kwargs): - if request.validation_errors: - return dict(error_with=request.validation_errors.keys()) + if request.pecan['validation_errors']: + return dict(error_with=request.pecan['validation_errors'].keys()) else: return kwargs @@ -639,7 +639,7 @@ class TestValidation(object): @expose(template='mako:form_login.html', schema=LoginSchema()) def index(self, **kwargs): - if request.validation_errors: + if request.pecan['validation_errors']: return dict() else: return dict(data=kwargs)