From 14f31880f3a044d3b688c36ecae1db3c99329925 Mon Sep 17 00:00:00 2001 From: Neil Williams Date: Tue, 8 Jan 2013 12:28:57 -0800 Subject: [PATCH] Replace c.response with pylons.response. pylons.response is its own StackedObjectProxy and returning response objects from controllers is deprecated so returning c.response is bad. In many cases, this transition just involves returning content from the controller directly rather than doing c.response.content = ___. This will make the transition to Pylons 0.9.7 easier because WebOb's response object doesn't have a content property and works differently. --- r2/r2/config/middleware.py | 12 ++--- r2/r2/controllers/buttons.py | 8 +-- r2/r2/controllers/captcha.py | 5 +- r2/r2/controllers/error.py | 20 +++----- r2/r2/controllers/front.py | 25 ++++------ r2/r2/controllers/mediaembed.py | 2 +- r2/r2/controllers/post.py | 7 +-- r2/r2/controllers/promotecontroller.py | 4 +- r2/r2/controllers/reddit_base.py | 69 +++++++++----------------- r2/r2/lib/base.py | 13 ++--- r2/r2/lib/jsonresponse.py | 2 +- r2/r2/lib/pages/pages.py | 3 +- r2/r2/lib/sup.py | 6 +-- r2/r2/lib/validator/validator.py | 9 +++- 14 files changed, 69 insertions(+), 116 deletions(-) diff --git a/r2/r2/config/middleware.py b/r2/r2/config/middleware.py index a275dd3e2..d90d5ae63 100644 --- a/r2/r2/config/middleware.py +++ b/r2/r2/config/middleware.py @@ -31,7 +31,7 @@ from paste.cascade import Cascade from paste.registry import RegistryManager from paste.urlparser import StaticURLParser from paste.deploy.converters import asbool -from pylons import config +from pylons import config, response from pylons.error import error_template from pylons.middleware import ErrorDocuments, ErrorHandler, StaticJavascripts from pylons.wsgiapp import PylonsApp, PylonsBaseWSGIApp @@ -86,14 +86,8 @@ def error_mapper(code, message, environ, global_conf=None, **kw): #preserve x-sup-id when 304ing if code == 304: - #check to see if c is useable - try: - c.test - except TypeError: - pass - else: - if c.response.headers.has_key('x-sup-id'): - d['x-sup-id'] = c.response.headers['x-sup-id'] + if response.headers.has_key('x-sup-id'): + d['x-sup-id'] = response.headers['x-sup-id'] extension = environ.get("extension") if extension: diff --git a/r2/r2/controllers/buttons.py b/r2/r2/controllers/buttons.py index b8111b8c1..ab82d6317 100644 --- a/r2/r2/controllers/buttons.py +++ b/r2/r2/controllers/buttons.py @@ -25,9 +25,9 @@ from r2.lib.pages import (ButtonLite, ButtonDemoPanel, WidgetDemoPanel, Bookmarklets, BoringPage) from r2.lib.pages.things import wrap_links from r2.models import * -from r2.lib.utils import tup +from r2.lib.utils import tup, to_js from r2.lib.validator import * -from pylons import c, request +from pylons import c, request, response from pylons.i18n import _ class ButtonsController(RedditController): @@ -81,7 +81,6 @@ class ButtonsController(RedditController): styled = VBoolean('styled', default=True)) def GET_button_lite(self, buttonimage, title, url, styled, newwindow): c.render_style = 'js' - c.response_content_type = 'text/javascript; charset=UTF-8' if not url: url = request.referer @@ -99,7 +98,8 @@ class ButtonsController(RedditController): styled = styled, **kw) bjs = self.get_wrapped_link(url, wrapper = builder_wrapper) - return self.sendjs(bjs.render(), callback='', escape=False) + response.content_type = "text/javascript" + return to_js(bjs.render(), callback='', escape=False) def GET_button_demo_page(self): # no buttons for domain listings -> redirect to top level diff --git a/r2/r2/controllers/captcha.py b/r2/r2/controllers/captcha.py index da0136a1e..e552d0b59 100644 --- a/r2/r2/controllers/captcha.py +++ b/r2/r2/controllers/captcha.py @@ -23,12 +23,13 @@ from reddit_base import RedditController import StringIO import r2.lib.captcha as captcha -from pylons import c +from pylons import c, response class CaptchaController(RedditController): def GET_captchaimg(self, iden): image = captcha.get_image(iden) f = StringIO.StringIO() image.save(f, "PNG") - return self.sendpng(f.getvalue()) + response.content_type = "image/png;" + return f.getvalue() diff --git a/r2/r2/controllers/error.py b/r2/r2/controllers/error.py index 6b3c22f4c..3095950b2 100644 --- a/r2/r2/controllers/error.py +++ b/r2/r2/controllers/error.py @@ -112,7 +112,6 @@ class ErrorController(RedditController): def send403(self): - c.response.status_code = 403 c.site = DefaultSR() if 'usable_error_content' in request.environ: return request.environ['usable_error_content'] @@ -124,15 +123,12 @@ class ErrorController(RedditController): return res.render() def send404(self): - c.response.status_code = 404 if 'usable_error_content' in request.environ: return request.environ['usable_error_content'] return pages.RedditError(_("page not found"), _("the page you requested does not exist")).render() def send429(self): - c.response.status_code = 429 - retry_after = request.environ.get("retry_after") if retry_after: response.headers["Retry-After"] = str(retry_after) @@ -145,8 +141,7 @@ class ErrorController(RedditController): return template.render(logo_url=static(g.default_header_url)) def send503(self): - c.response.status_code = 503 - c.response.headers['Retry-After'] = request.environ['retry_after'] + response.headers["Retry-After"] = str(request.environ["retry_after"]) return request.environ['usable_error_content'] def GET_document(self): @@ -167,13 +162,12 @@ class ErrorController(RedditController): c.site = Subreddit._by_name(srname) if c.render_style not in self.allowed_render_styles: if code not in (204, 304): - c.response.content = str(code) - c.response.status_code = code - return c.response + return str(code) + else: + return "" elif c.render_style in extensions.API_TYPES: data = request.environ.get('extra_error_data', {'error': code}) - c.response.content = websafe_json(json.dumps(data)) - return c.response + return websafe_json(json.dumps(data)) elif takedown and code == 404: link = Link._by_fullname(takedown) return pages.TakedownPage(link).render() @@ -192,8 +186,8 @@ class ErrorController(RedditController): if request.GET.has_key('x-sup-id'): x_sup_id = request.GET.get('x-sup-id') if '\r\n' not in x_sup_id: - c.response.headers['x-sup-id'] = x_sup_id - return c.response + response.headers['x-sup-id'] = x_sup_id + return "" elif c.site: return self.send404() else: diff --git a/r2/r2/controllers/front.py b/r2/r2/controllers/front.py index ddc1bf6fc..3d94387aa 100755 --- a/r2/r2/controllers/front.py +++ b/r2/r2/controllers/front.py @@ -49,7 +49,7 @@ from r2.lib.errors import errors from listingcontroller import ListingController from oauth2 import OAuth2ResourceController, require_oauth2_scope from api_docs import api_doc, api_section -from pylons import c, request, request +from pylons import c, request, response from r2.models.token import EmailVerificationToken from r2.controllers.ipn import generate_blob @@ -410,11 +410,10 @@ class FrontController(RedditController, OAuth2ResourceController): must_revalidate=False, ) - c.response_content_type = 'text/css' - c.response.content = stylesheet_contents + response.content_type = 'text/css' if c.site.type == 'private': - c.response.headers['X-Private-Subreddit'] = 'private' - return c.response + response.headers['X-Private-Subreddit'] = 'private' + return stylesheet_contents else: return self.abort404() @@ -979,8 +978,7 @@ class FrontController(RedditController, OAuth2ResourceController): sup.set_expires_header() if c.extension == 'json': - c.response.content = sup.sup_json(period) - return c.response + return sup.sup_json(period) else: return self.abort404() @@ -991,8 +989,7 @@ class FrontController(RedditController, OAuth2ResourceController): def GET_traffic(self, article): content = trafficpages.PromotedLinkTraffic(article) if c.render_style == 'csv': - c.response.content = content.as_csv() - return c.response + return content.as_csv() return LinkInfoPage(link=article, page_classes=["promoted-traffic"], @@ -1005,8 +1002,7 @@ class FrontController(RedditController, OAuth2ResourceController): if link: content = trafficpages.PromoTraffic(link) if c.render_style == 'csv': - c.response.content = content.as_csv() - return c.response + return content.as_csv() return LinkInfoPage(link=link, page_classes=["promo-traffic"], comment=None, @@ -1237,14 +1233,13 @@ class FormsController(RedditController): def GET_validuser(self): """checks login cookie to verify that a user is logged in and returns their user name""" - c.response_content_type = 'text/plain' + response.content_type = 'text/plain' if c.user_is_loggedin: # Change cookie based on can_wiki trac permissions perm = str(c.user.can_wiki(default=False)) - c.response.content = c.user.name + "," + perm + return c.user.name + "," + perm else: - c.response.content = '' - return c.response + return "" def _render_opt_in_out(self, msg_hash, leave): """Generates the form for an optin/optout page""" diff --git a/r2/r2/controllers/mediaembed.py b/r2/r2/controllers/mediaembed.py index 62c0f7b4b..8f2b4a833 100644 --- a/r2/r2/controllers/mediaembed.py +++ b/r2/r2/controllers/mediaembed.py @@ -56,7 +56,7 @@ class MediaembedController(MinimalController): class AdController(MinimalController): def request_key(self): - return make_key('request_key', + return make_key('request_', c.lang, c.content_langs, request.host, diff --git a/r2/r2/controllers/post.py b/r2/r2/controllers/post.py index 36dbb40ef..40a361b3a 100644 --- a/r2/r2/controllers/post.py +++ b/r2/r2/controllers/post.py @@ -32,9 +32,6 @@ from r2.models import * import hashlib class PostController(ApiController): - def api_wrapper(self, kw): - return Storage(**kw) - def set_options(self, all_langs, pref_lang, **kw): if c.errors.errors: print "fucker" @@ -180,7 +177,7 @@ class PostController(ApiController): def POST_login(self, dest, *a, **kw): ApiController._handle_login(self, *a, **kw) c.render_style = "html" - c.response_content_type = "" + response.content_type = "text/html" if c.errors: return LoginPage(user_login = request.post.get('user'), @@ -192,7 +189,7 @@ class PostController(ApiController): def POST_reg(self, dest, *a, **kw): ApiController._handle_register(self, *a, **kw) c.render_style = "html" - c.response_content_type = "" + response.content_type = "text/html" if c.errors: return LoginPage(user_reg = request.post.get('user'), diff --git a/r2/r2/controllers/promotecontroller.py b/r2/r2/controllers/promotecontroller.py index bf2153ce2..51d1f6372 100644 --- a/r2/r2/controllers/promotecontroller.py +++ b/r2/r2/controllers/promotecontroller.py @@ -184,11 +184,9 @@ class PromoteController(ListingController): def GET_admingraph(self): content = Promote_Graph(admin_view=True) if c.render_style == 'csv': - c.response.content = content.as_csv() - return c.response + return content.as_csv() return PromotePage("admingraph", content=content).render() - def GET_inventory(self, sr_name): ''' Return available inventory data as json for use in ajax calls diff --git a/r2/r2/controllers/reddit_base.py b/r2/r2/controllers/reddit_base.py index ef53d1726..e43ba2a3c 100644 --- a/r2/r2/controllers/reddit_base.py +++ b/r2/r2/controllers/reddit_base.py @@ -53,6 +53,7 @@ from r2.lib.errors import ( ForbiddenError, errors, ) +from r2.lib.filters import _force_utf8 from r2.lib.strings import strings from r2.lib.template_helpers import add_sr from r2.lib.tracking import encrypt, decrypt @@ -63,6 +64,7 @@ from r2.lib.utils import ( http_utils, is_subdomain, is_throttled, + tup, ) from r2.lib.validator import ( build_arg_list, @@ -368,7 +370,7 @@ def set_subreddit(): def set_content_type(): e = request.environ c.render_style = e['render_style'] - c.response_content_type = e['content_type'] + response.content_type = e['content_type'] if e.has_key('extension'): c.extension = ext = e['extension'] @@ -376,7 +378,7 @@ def set_content_type(): def to_js(content): return utils.to_js(content, callback=request.params.get( "callback", "document.write")) - c.response_wrappers.append(to_js) + c.response_wrapper = to_js if ext in ("rss", "api", "json") and request.method.upper() == "GET": user = valid_feed(request.GET.get("user"), request.GET.get("feed"), @@ -591,10 +593,9 @@ def cross_domain(origin_check=is_trusted_origin, **options): if cors_perms["origin_check"](g.origin): name = request.environ["pylons.routes_dict"]["action_name"] resp = fn(self, *args, **kwargs) - c.cookies.add('hoist_%s' % name, ''.join(resp.content)) - c.response_content_type = 'text/html' - resp.content = '' - return resp + c.cookies.add('hoist_%s' % name, ''.join(tup(resp))) + response.content_type = 'text/html' + return "" else: abort(403) else: @@ -638,7 +639,7 @@ class MinimalController(BaseController): except CookieError: cookies_key = '' - return make_key('request_key_', + return make_key('request_', c.lang, c.content_langs, request.host, @@ -652,7 +653,7 @@ class MinimalController(BaseController): cookies_key) def cached_response(self): - return c.response + return response.content def pre(self): action = request.environ["pylons.routes_dict"].get("action") @@ -661,7 +662,7 @@ class MinimalController(BaseController): else: c.request_timer = SimpleSillyStub() - c.response_wrappers = [] + c.response_wrapper = None c.start_time = datetime.now(g.tz) c.request_timer.start() g.reset_caches() @@ -692,7 +693,6 @@ class MinimalController(BaseController): r = g.pagecache.get(self.request_key()) if r: r, c.cookies = r - response = c.response response.headers = r.headers response.content = r.content @@ -712,24 +712,19 @@ class MinimalController(BaseController): c.request_timer.name = request_timer_name("cached_response") # make sure to carry over the content type - c.response_content_type = r.headers['content-type'] + response.content_type = r.headers['content-type'] c.used_cache = True # response wrappers have already been applied before cache write - c.response_wrappers = [] - + c.response_wrapper = None def post(self): c.request_timer.intermediate("action") - response = c.response - content = filter(None, response.content) - if isinstance(content, (list, tuple)): - content = ''.join(content) - for w in c.response_wrappers: - content = w(content) - response.content = content - if c.response_content_type: - response.headers['Content-Type'] = c.response_content_type + if c.response_wrapper: + content = "".join(_force_utf8(x) + for x in tup(response.content) if x) + wrapped_content = c.response_wrapper(content) + response.content = wrapped_content if c.user_is_loggedin and not c.allow_loggedin_cache: response.headers['Cache-Control'] = 'no-cache' @@ -738,18 +733,16 @@ class MinimalController(BaseController): if c.deny_frames: response.headers["X-Frame-Options"] = "DENY" - #return #set content cache if (g.page_cache_time and request.method.upper() == 'GET' and (not c.user_is_loggedin or c.allow_loggedin_cache) and not c.used_cache - and response.status_code not in (429, 503) - and response.content and response.content[0]): + and response.status_code not in (429, 503)): try: g.pagecache.set(self.request_key(), - (response, c.cookies), - g.page_cache_time) + (response._current_obj(), c.cookies), + g.page_cache_time) except MemcachedError as e: # this codepath will actually never be hit as long as # the pagecache memcached client is in no_reply mode. @@ -819,11 +812,6 @@ class MinimalController(BaseController): """Return empty responses for CORS preflight requests""" self.check_cors() - def sendpng(self, string): - c.response_content_type = 'image/png' - c.response.content = string - return c.response - def update_qstring(self, dict): merged = copy(request.get) merged.update(dict) @@ -831,18 +819,7 @@ class MinimalController(BaseController): def api_wrapper(self, kw): data = simplejson.dumps(kw) - c.response.content = filters.websafe_json(data) - return c.response - - def iframe_api_wrapper(self, kw): - data = simplejson.dumps(kw) - c.response_content_type = 'text/html' - c.response.content = ( - '') % filters.websafe_json(data) - return c.response + return filters.websafe_json(data) class RedditController(MinimalController): @@ -1036,7 +1013,7 @@ class RedditController(MinimalController): last_modified = last_modified.replace(microsecond=0) date_str = http_utils.http_date_str(last_modified) - c.response.headers['last-modified'] = date_str + response.headers['last-modified'] = date_str cache_control = [] if private: @@ -1044,7 +1021,7 @@ class RedditController(MinimalController): cache_control.append('max-age=%d' % max_age.total_seconds()) if must_revalidate: cache_control.append('must-revalidate') - c.response.headers['cache-control'] = ', '.join(cache_control) + response.headers['cache-control'] = ', '.join(cache_control) modified_since = request.if_modified_since if modified_since and modified_since >= last_modified: diff --git a/r2/r2/lib/base.py b/r2/r2/lib/base.py index 880e595c8..0f4b951c1 100644 --- a/r2/r2/lib/base.py +++ b/r2/r2/lib/base.py @@ -24,7 +24,7 @@ import _pylibmc import pycassa.pool import sqlalchemy.exc -from pylons import Response, c, g, request, session, config +from pylons import c, g, request, session, config, response from pylons.controllers import WSGIController, Controller from pylons.i18n import N_, _, ungettext, get_lang from paste import httpexceptions @@ -141,7 +141,6 @@ class BaseController(WSGIController): request.environ['pylons.routes_dict']['action_name'] = action request.environ['pylons.routes_dict']['action'] = handler_name - c.response = Response() try: res = WSGIController.__call__(self, environ, start_response) except Exception as e: @@ -221,14 +220,8 @@ class BaseController(WSGIController): sends the user to that location with the provided HTTP code. """ dest = cls.format_output_url(dest or "/") - c.response.headers['Location'] = dest - c.response.status_code = code - return c.response - - def sendjs(self,js, callback="document.write", escape=True): - c.response.headers['Content-Type'] = 'text/javascript' - c.response.content = to_js(js, callback, escape) - return c.response + response.status_code = code + response.headers['Location'] = dest class EmbedHandler(urllib2.BaseHandler, urllib2.HTTPHandler, urllib2.HTTPErrorProcessor, urllib2.HTTPDefaultErrorHandler): diff --git a/r2/r2/lib/jsonresponse.py b/r2/r2/lib/jsonresponse.py index abffa43ef..bc86ff53e 100644 --- a/r2/r2/lib/jsonresponse.py +++ b/r2/r2/lib/jsonresponse.py @@ -40,7 +40,7 @@ class JsonResponse(object): api func. """ - content_type = 'application/json; charset=UTF-8' + content_type = 'application/json' def __init__(self): self._clear() diff --git a/r2/r2/lib/pages/pages.py b/r2/r2/lib/pages/pages.py index b16919e7f..af322150e 100755 --- a/r2/r2/lib/pages/pages.py +++ b/r2/r2/lib/pages/pages.py @@ -93,8 +93,7 @@ def responsive(res, space_compress = False): res = "%s(%s)" % (websafe_json(c.allowed_callback), res) elif space_compress: res = spaceCompress(res) - c.response.content = res - return c.response + return res class Reddit(Templated): '''Base class for rendering a page on reddit. Handles toolbar creation, diff --git a/r2/r2/lib/sup.py b/r2/r2/lib/sup.py index 2230ade15..50efe2e3c 100644 --- a/r2/r2/lib/sup.py +++ b/r2/r2/lib/sup.py @@ -30,7 +30,7 @@ import simplejson from r2.lib.utils import rfc3339_date_str, http_date_str, to36 from r2.lib.memoize import memoize from r2.lib.template_helpers import get_domain -from pylons import g, c +from pylons import g, c, response PERIODS = [600, 300, 60] MIN_PERIOD = min(PERIODS) @@ -104,10 +104,10 @@ def sup_json(period): def set_sup_header(user, action): sup_id = make_sup_id(user, action) - c.response.headers['x-sup-id'] = sup_url() + '#' + sup_id + response.headers['x-sup-id'] = sup_url() + '#' + sup_id def set_expires_header(): seconds = make_cur_time(MIN_PERIOD) + MIN_PERIOD expire_time = datetime.fromtimestamp(seconds, g.tz) - c.response.headers['expires'] = http_date_str(expire_time) + response.headers['expires'] = http_date_str(expire_time) diff --git a/r2/r2/lib/validator/validator.py b/r2/r2/lib/validator/validator.py index 55ffb817c..819afe90c 100644 --- a/r2/r2/lib/validator/validator.py +++ b/r2/r2/lib/validator/validator.py @@ -213,7 +213,7 @@ def api_validate(response_type=None): else: responder = JsonResponse() - c.response_content_type = responder.content_type + response.content_type = responder.content_type try: kw = _make_validated_kw(fn, simple_vals, param_vals, env) @@ -291,7 +291,12 @@ def validatedMultipartForm(self, self_method, responder, simple_vals, if val: return val else: - return self.iframe_api_wrapper(responder.make_response()) + data = simplejson.dumps(responder.make_response()) + response.content_type = "text/html" + return ('') % filters.websafe_json(data) return _validatedForm(self, wrapped_self_method, responder, simple_vals, param_vals, *a, **kw)