response._view_environment has to contain all the web2py globals, including the response object, so all of them will be available for use when rendering the view. That means that when you return a dictionary to a view, it cannot contain any top-level keys whose names clash with any of the global objects, or they will be overwritten. This is generally not a problem when rendering HTML because you're not rendering the keys in that case (so the names don't really matter), but it can be an issue with JSON. In this case, it's probably better to use the JSON service or generate the JSON in the controller and return it directly rather than using the view to generate it.
Anthony On Sunday, December 2, 2012 1:10:13 AM UTC-5, Chris wrote: > > I've found a strange behavior in 1.99.7 (which is the same in 2.2.1 except > for a code cleanup issue). This is either a bug or I'm confused (don't > rule that out). This is a long message -- I thought it was important to > include a detailed demo of the phenomenon. > > The idea of generic views is wonderful -- a controller function can return > a dict; and based on the extension used to request the page, data is > returned using the formatting logic of the generic view of that type. This > works well and is demonstrated clearly in section 10.1.1 of the book. A > simple example: > > > In default.py ---------------- > > def test(): > return dict(status='200 OK') > > > Then from a unix command line ---------------- > > $ curl -L 'http://localhost:8000/default/test.load' > 200 OK > > $ curl -L 'http://localhost:8000/default/test.xml' > <?xml version="1.0" encoding="UTF-8"?><document><status>200 OK</status></ > document> > > $ curl -L 'http://localhost:8000/default/test.json' > { > "status": "200 OK" > } > > > ## note in JSON, I made a change in gluon / serializers.py to add indents > > So far so good. But something bad happens when the dictionary is nested: > > > In updated default.py ---------------- > > def test(): > return dict(response=dict(status='200 OK')) > > > requesting ...test.json creates an error file in my app/err folder > ---------------- > > Traceback (most recent call last): > File "/Users/chris/dev/hsmaster/web2py_v1.99.7/gluon/main.py", line 508, > in wsgibase > serve_controller(request, response, session) > File "/Users/chris/dev/hsmaster/web2py_v1.99.7/gluon/main.py", line 207, > in serve_controller > run_view_in(response._view_environment) > File "/Users/chris/dev/hsmaster/web2py_v1.99.7/gluon/compileapp.py",line > 596, in run_view_in > badv = \'invalid view (%s)\' % response.view > AttributeError: \'dict\' object has no attribute \'view\' > > > Observations ---------------- > > The book says "Any dictionary can be rendered in HTML, XML and JSON as > long as it only contains python primitive types (int, float, string, list, > tuple, dictionary)." so this seems in-bounds. json.dumps (serializer > users) has no problem with nested dicts. > > This may be a clue -- if we service-enable the same function and call > differently ... > > @service.json > def test(): > return dict(response=dict(status='200 OK')) > > > ... the result is fine ... > > $ curl -L 'http://localhost:8000/default/call/json/test' > { > "response": { > "status": "200 OK" > } > } > > > or if we don't service-enable it, call like we did the first time, but > change the name of the top-level key like this: > > def test(): > return dict(blahblah=dict(status='200 OK')) > > > ... the result is fine too ... > > $ curl -L 'http://localhost:8000/default/test.json' > { > "blahblah": { > "status": "200 OK" > } > } > > > Conclusion ---------------- > > After a long Wingare debugging session, I think this code is the proximate > cause of the problem: > > in gluon / main.py / serve_controller(): > > ... > page = run_controller_in(request.controller, request.function,environment > ) > if isinstance(page, dict): > response._vars = page > for key in page: > response._view_environment[key] = page[key] ### AAA > run_view_in(response._view_environment) > page = response.body.getvalue() > ... > > > Basically it appears that the line marked AAA overwrites data in the > response._view_environment structure with data from the dict the test() > function returns. The problem only happens when a key in the top level > (not nested) of the dict matches an existing key in > response._view_environment. response._view_environment has 129 keys: A B > BEAUTIFY ... auth cache crud ... response request service etc.) and when > one of these is in common with the dict being rendered, the value of > dict[key] overwrites it. > > The traceback above was from gluon / compileapp.py / run_view_in() -- > that relies on response._view_environment['response'] to have a key 'view', > which is present before line AAA; but gone after > -- response._view_environment['response'] has been clobbered. > > > Remediation ---------------- > > In my debugging, I changed line AAA to be a pass statement and nothing bad > happened. I don't know if that is the right approach or not. The same > little block of code I showed above (from main.py) also occurs twice in > compileapp.py. I don't know enough to judge whether eliminating line AAA > as I did is a good idea or not; or if it should be done in some or all of > the three location. > > > Request ---------------- > > Can someone say if my solution is on the right track; and why the dict is > being merged with the response? Maybe the right approach for a different > payload -- but dicts can't avoid using these keys. view, title, status, db > -- these are pretty common keys. > > > Best regards and thanks -- > --

