diff --git a/r2/r2/controllers/listingcontroller.py b/r2/r2/controllers/listingcontroller.py index 261c73ed3..8b3441236 100755 --- a/r2/r2/controllers/listingcontroller.py +++ b/r2/r2/controllers/listingcontroller.py @@ -62,7 +62,7 @@ class ListingController(RedditController, OAuth2ResourceController): # allow stylesheets on listings allow_stylesheets = True - # toggles showing numbers + # toggles showing numbers show_nums = True # any text that should be shown on the top of the page @@ -122,7 +122,7 @@ class ListingController(RedditController, OAuth2ResourceController): def content(self): """Renderable object which will end up as content of the render_cls""" return self.listing_obj - + def query(self): """Query to execute to generate the listing""" raise NotImplementedError @@ -165,7 +165,7 @@ class ListingController(RedditController, OAuth2ResourceController): def listing(self): """Listing to generate from the builder""" - if (getattr(c.site, "_id", -1) == get_promote_srid() and + if (getattr(c.site, "_id", -1) == get_promote_srid() and not c.user_is_sponsor): abort(403, 'forbidden') pane = LinkListing(self.builder_obj, show_nums = self.show_nums).listing() @@ -210,7 +210,7 @@ class FixListing(object): if self.after and self.after._hot == 0: self.abort404() - #don't draw next/prev links for + #don't draw next/prev links for if listing.things: if listing.things[-1]._hot == 0: listing.next = None @@ -365,7 +365,7 @@ class HotController(FixListing, ListingController): spotlight = None show_sponsors = not (not c.user.pref_show_sponsors and c.user.gold) show_organic = c.user.pref_organic - on_frontpage = isinstance(c.site, DefaultSR) + on_frontpage = isinstance(c.site, DefaultSR) if self.requested_ad: spotlight = self.make_requested_ad() @@ -560,7 +560,7 @@ class UserController(ListingController): for srname in srnames: sr_buttons.append(NavButton(srname, srname, opt='sr')) base_path = request.path - sr_menu = NavMenu(sr_buttons, base_path=base_path, + sr_menu = NavMenu(sr_buttons, base_path=base_path, title=_('filter by subreddit'), type='lightdrop') res.append(sr_menu) @@ -680,7 +680,7 @@ class UserController(ListingController): if where in ('liked', 'disliked') and not votes_visible(vuser): return self.abort403() - if (where in ('saved', 'hidden') and not + if (where in ('saved', 'hidden') and not ((c.user_is_loggedin and c.user._id == vuser._id) or c.user_is_admin)): return self.abort403() @@ -833,7 +833,7 @@ class MessageController(ListingController): return ListingController.builder(self) def listing(self): - if (self.where == 'messages' and + if (self.where == 'messages' and (c.user.pref_threaded_messages or self.message)): return Listing(self.builder_obj).listing() pane = ListingController.listing(self) diff --git a/r2/r2/lib/db/tdb_cassandra.py b/r2/r2/lib/db/tdb_cassandra.py index af5c106da..9a4118961 100644 --- a/r2/r2/lib/db/tdb_cassandra.py +++ b/r2/r2/lib/db/tdb_cassandra.py @@ -116,7 +116,7 @@ def get_manager(seeds): class ThingMeta(type): def __init__(cls, name, bases, dct): type.__init__(cls, name, bases, dct) - + if hasattr(cls, '_ttl') and hasattr(cls._ttl, 'total_seconds'): cls._ttl = cls._ttl.total_seconds() @@ -772,17 +772,16 @@ class ThingBase(object): comm_str, part_str) if debug: - # we only want this with g.debug because overriding __del__ - # can play hell with memory leaks + # we only want this with g.debug because overriding __del__ can play + # hell with memory leaks def __del__(self): - if not self._committed: - # normally we'd log this with g.log or something, - # but we can't guarantee that the thread - # destructing us has access to g - print "Warning: discarding uncomitted %r; this is usually a bug" % (self,) - elif self._dirty: - print ("Warning: discarding dirty %r; this is usually a bug (_dirties=%r, _deletes=%r)" - % (self,self._dirties,self._deletes)) + if not self._committed: + # normally we'd log this with g.log or something, but we can't + # guarantee that the thread destructing us has access to g + print "Warning: discarding uncomitted %r; this is usually a bug" % (self,) + elif self._dirty: + print ("Warning: discarding dirty %r; this is usually a bug (_dirties=%r, _deletes=%r)" + % (self,self._dirties,self._deletes)) class Thing(ThingBase): _timestamp_prop = 'date' @@ -948,8 +947,8 @@ class ColumnQuery(object): """ _chunk_size = 100 - def __init__(self, cls, rowkeys, column_start="", column_finish="", - column_count=100, column_reversed=True, + def __init__(self, cls, rowkeys, column_start="", column_finish="", + column_count=100, column_reversed=True, column_to_obj=None, obj_to_column=None): self.cls = cls @@ -978,10 +977,10 @@ class ColumnQuery(object): @staticmethod def default_column_to_obj(columns): """ - Mapping from column --> object. - - This default doesn't actually return the underlying object but we don't - know how to do that without more information. + Mapping from column --> object. + + This default doesn't actually return the underlying object but we don't + know how to do that without more information. """ return columns @@ -1074,7 +1073,7 @@ class ColumnQuery(object): yield r def __repr__(self): - return "<%s(%s-%r)>" % (self.__class__.__name__, self.cls.__name__, + return "<%s(%s-%r)>" % (self.__class__.__name__, self.cls.__name__, self.rowkeys) class MultiColumnQuery(object): @@ -1104,7 +1103,7 @@ class MultiColumnQuery(object): if self.sort_key: def sort_key(tup): - # Need to point the supplied sort key at the correct item in + # Need to point the supplied sort key at the correct item in # the (sortable, item, generator) tuple return self.sort_key(tup[0]) else: @@ -1224,7 +1223,7 @@ class Query(object): class View(ThingBase): # Views are Things like any other, but may have special key - # characteristics. Uses ColumnQuery for queries across a row. + # characteristics. Uses ColumnQuery for queries across a row. _timestamp_prop = None _value_type = 'str' @@ -1243,7 +1242,7 @@ class View(ThingBase): @classmethod def _obj_to_column(cls, objs): - """Mapping from _view_of object --> view column. Returns a + """Mapping from _view_of object --> view column. Returns a single item dict {column name:column value} or list of dicts.""" objs, is_single = tup(objs, ret_is_single=True) @@ -1279,7 +1278,7 @@ class View(ThingBase): column_reversed = not reverse # Reverse convention for cassandra is opposite - q = cls._query_cls(cls, rowkeys, column_count=count, + q = cls._query_cls(cls, rowkeys, column_count=count, column_reversed=column_reversed, column_to_obj=cls._column_to_obj, obj_to_column=cls._obj_to_column) @@ -1327,7 +1326,7 @@ class View(ThingBase): # can we be smarter here? thing_cache.delete(cls._cache_key_id(row_key)) - + @classmethod @will_write def _remove(cls, key, columns): diff --git a/r2/r2/lib/pages/pages.py b/r2/r2/lib/pages/pages.py index c85039304..e60c2d8b1 100755 --- a/r2/r2/lib/pages/pages.py +++ b/r2/r2/lib/pages/pages.py @@ -124,7 +124,7 @@ class Reddit(Templated): loginbox -- enable/disable rendering of the small login box in the right margin (only if no user is logged in; login box will be disabled for a logged in user) show_sidebar -- enable/disable content in the right margin - + infotext -- text to display in a

above the content nav_menus -- list of Menu objects to be shown in the area below the header content -- renderable object to fill the main content well in the page. @@ -152,7 +152,7 @@ class Reddit(Templated): extra_stylesheets = [] def __init__(self, space_compress = True, nav_menus = None, loginbox = True, - infotext = '', content = None, short_description='', title = '', robots = None, + infotext = '', content = None, short_description='', title = '', robots = None, show_sidebar = True, footer = True, srbar = True, page_classes = None, show_wiki_actions = False, extra_js_config = None, **context): Templated.__init__(self, **context) @@ -233,25 +233,25 @@ class Reddit(Templated): self._content = content self.toolbars = self.build_toolbars() - + def wiki_actions_menu(self, moderator=False): buttons = [] - - buttons.append(NamedButton("wikirecentrevisions", + + buttons.append(NamedButton("wikirecentrevisions", css_class="wikiaction-revisions", dest="/wiki/revisions")) - - buttons.append(NamedButton("wikipageslist", + + buttons.append(NamedButton("wikipageslist", css_class="wikiaction-pages", dest="/wiki/pages")) if moderator: - buttons += [NamedButton('wikibanned', css_class='reddit-ban', + buttons += [NamedButton('wikibanned', css_class='reddit-ban', dest='/about/wikibanned'), - NamedButton('wikicontributors', - css_class='reddit-contributors', + NamedButton('wikicontributors', + css_class='reddit-contributors', dest='/about/wikicontributors') ] - + return SideContentBox(_('wiki tools'), [NavMenu(buttons, type="flat_vert", @@ -259,7 +259,7 @@ class Reddit(Templated): separator="")], _id="wikiactions", collapsible=True) - + def sr_admin_menu(self): buttons = [] is_single_subreddit = not isinstance(c.site, (ModSR, MultiReddit)) @@ -416,7 +416,7 @@ class Reddit(Templated): # don't show the subreddit info bar on cnames unless the option is set if not isinstance(c.site, FakeSubreddit) and (not c.cname or c.site.show_cname_sidebar): ps.append(SubredditInfoBar()) - moderator = c.user_is_loggedin and (c.user_is_admin or + moderator = c.user_is_loggedin and (c.user_is_admin or c.site.is_moderator(c.user)) wiki_moderator = c.user_is_loggedin and ( c.user_is_admin @@ -456,7 +456,7 @@ class Reddit(Templated): helplink = ("/message/compose?to=%%2Fr%%2F%s" % c.site.name, label) ps.append(SideContentBox(_('moderators'), moderators, - helplink = helplink, + helplink = helplink, more_href = mod_href, more_text = more_text)) @@ -484,7 +484,7 @@ class Reddit(Templated): """ res = Templated.render(self, *a, **kw) return responsive(res, self.space_compress) - + def corner_buttons(self): """set up for buttons in upper right corner of main page.""" buttons = [] @@ -524,7 +524,7 @@ class Reddit(Templated): NamedButton('comments')] else: main_buttons = [NamedButton('hot', dest='', aliases=['/hot']), - NamedButton('new'), + NamedButton('new'), NamedButton('rising'), NamedButton('controversial'), NamedButton('top'), @@ -795,7 +795,7 @@ class PrefsPage(Reddit): #if CustomerID.get_id(user): # buttons += [NamedButton('payment')] buttons += [NamedButton('delete')] - return [PageNameNav('nomenu', title = _("preferences")), + return [PageNameNav('nomenu', title = _("preferences")), NavMenu(buttons, base_path = "/prefs", type="tabmenu")] class PrefOptions(Templated): @@ -842,7 +842,7 @@ class MessagePage(Reddit): self.replybox = UserText(item = None, creating = True, post_form = 'comment', display = False, cloneable = True) - + def content(self): return self.content_stack((self.replybox, @@ -866,12 +866,12 @@ class MessagePage(Reddit): _("%(site)s mail") % {'site': c.site.name}, "moderator", aliases = ["/about/message/inbox", "/about/message/unread"])) - return [PageNameNav('nomenu', title = _("message")), + return [PageNameNav('nomenu', title = _("message")), NavMenu(buttons, base_path = "/message", type="tabmenu")] class MessageCompose(Templated): """Compose message form.""" - def __init__(self,to='', subject='', message='', success='', + def __init__(self,to='', subject='', message='', success='', captcha = None): from r2.models.admintools import admintools @@ -880,15 +880,15 @@ class MessageCompose(Templated): captcha = captcha, admins = admintools.admin_list()) - + class BoringPage(Reddit): """parent class For rendering all sorts of uninteresting, sortless, navless form-centric pages. The top navmenu is populated only with the text provided with pagename and the page title is 'reddit.com: pagename'""" - + extension_handling= False - + def __init__(self, pagename, css_class=None, **context): self.pagename = pagename name = c.site.name or g.default_sr @@ -916,7 +916,7 @@ class FormPage(BoringPage): def __init__(self, pagename, show_sidebar = False, *a, **kw): BoringPage.__init__(self, pagename, show_sidebar = show_sidebar, *a, **kw) - + class LoginPage(BoringPage): enable_login_cover = False @@ -1052,7 +1052,7 @@ class SearchPage(BoringPage): class TakedownPage(BoringPage): def __init__(self, link): - BoringPage.__init__(self, getattr(link, "takedown_title", _("bummer")), + BoringPage.__init__(self, getattr(link, "takedown_title", _("bummer")), content = TakedownPane(link)) def render(self, *a, **kw): @@ -1063,7 +1063,7 @@ class TakedownPage(BoringPage): class TakedownPane(Templated): def __init__(self, link, *a, **kw): self.link = link - self.explanation = getattr(self.link, "explanation", + self.explanation = getattr(self.link, "explanation", _("this page is no longer available due to a copyright claim.")) Templated.__init__(self, *a, **kw) @@ -1131,7 +1131,7 @@ class LinkInfoPage(Reddit): params = {'author' : author.name, 'title' : _force_unicode(link_title)} title = strings.permalink_title % params short_description = _truncate(comment.body.strip(), MAX_DESCRIPTION_LENGTH) if comment.body else None - + self.subtitle = subtitle @@ -1236,7 +1236,7 @@ class CommentPane(Templated): num = self.article.num_comments # bit of triage: we don't care about 10% changes in comment # trees once they get to a certain length. The cache is only a few - # min long anyway. + # min long anyway. if num > 1000: num = (num / 100) * 100 elif num > 100: @@ -1407,7 +1407,7 @@ class EditReddit(Reddit): _('about %(site)s') % dict(site=c.site.name)) Reddit.__init__(self, title=title, *a, **kw) - + def build_toolbars(self): if not c.cname: return [PageNameNav('subreddit', title=self.title)] @@ -1473,7 +1473,7 @@ class SubredditsPage(Reddit): class MySubredditsPage(SubredditsPage): """Same functionality as SubredditsPage, without the search box.""" - + def content(self): return self.content_stack((self.nav_menu, self.infobar, self._content)) @@ -1597,7 +1597,7 @@ class ProfileBar(Templated): self.gold_remaining = _("less than a day") else: # "X months, Y days" if less than 2 months left, otherwise "X months" - precision = 60 * 60 * 24 * 30 if gold_days_left > 60 else 60 * 60 * 24 + precision = 60 * 60 * 24 * 30 if gold_days_left > 60 else 60 * 60 * 24 self.gold_remaining = timeuntil(self.gold_expiration, precision) if hasattr(user, "gold_subscr_id"): @@ -1663,7 +1663,7 @@ class RedditError(BoringPage): def __init__(self, title, message, image=None, sr_description=None, explanation=None): BoringPage.__init__(self, title, loginbox=False, - show_sidebar = False, + show_sidebar = False, content=ErrorPage(title=title, message=message, image=image, @@ -1777,7 +1777,7 @@ class SubredditTopBar(CachedTemplate): for sr in reddits], type = 'flatlist', separator = '-', css_class = 'sr-bar') - + def sr_bar (self): sep = ' | ' menus = [] @@ -2144,7 +2144,7 @@ class SearchBar(Templated): def __init__(self, header=None, num_results=0, prev_search='', elapsed_time=0, search_params={}, show_feedback=False, simple=False, restrict_sr=False, site=None, syntax=None, - subreddit_search=False, converted_data=None, facets={}, + subreddit_search=False, converted_data=None, facets={}, sort=None, recent=None, **kw): if header is None: header = _("previous search") @@ -2208,7 +2208,7 @@ class FrameToolbar(Wrapped): Wrapped.__init__(self, link) if link is None: self.add_props(c.user, [self]) - + @classmethod def add_props(cls, user, wrapped): # unlike most wrappers we can guarantee that there is a link @@ -2257,7 +2257,7 @@ class NewLink(Templated): if self.show_self and self.show_link: all_fields = set(chain(*(parts for (tab, parts) in tabs))) buttons = [] - + if selftext == 'true' or text != '': self.default_tab = tabs[1][0] else: @@ -2295,7 +2295,7 @@ class ShareLink(CachedTemplate): Templated.__init__(self, link_name = link_name, emails = c.user.recent_share_emails()) - + class Share(Templated): pass @@ -2988,7 +2988,7 @@ class EnemyList(UserList): """Blacklist on /pref/friends""" type = 'enemy' cells = ('user', 'remove') - + def __init__(self, editable=True, addable=False): UserList.__init__(self, editable, addable) @@ -3135,7 +3135,7 @@ class BannedList(UserList): def user_ids(self): return self.rels.keys() - + class WikiBannedList(BannedList): """List of users banned from editing a given wiki""" type = 'wikibanned' @@ -3293,7 +3293,7 @@ class PromoteLinkFormCpm(PromoteLinkForm): def __init__(self, sr=None, link=None, listing='', timedeltatext='', *a, **kw): self.setup(sr, link, listing, timedeltatext, *a, **kw) - + if not c.user_is_sponsor: self.now = promote.promo_datetime_now().date() start_date = self.now @@ -3313,7 +3313,7 @@ class PromoAdminTool(Reddit): self.end = end if end else self.start + datetime.timedelta(1) # started_on shows promos that were scheduled to launch on start date if query_type == "started_on" and self.start: - all_promos = self.get_promo_info(self.start, + all_promos = self.get_promo_info(self.start, self.start + datetime.timedelta(1)) # exactly one day promos = {} start_date_string = self.start.strftime("%Y/%m/%d") @@ -3326,13 +3326,13 @@ class PromoAdminTool(Reddit): promos = self.get_promo_info(self.start, self.end) else: promos = {} - + for camp_id, promo in promos.iteritems(): link_id36 = promo["link_fullname"].split('_')[1] promo["campaign_id"] = camp_id promo["edit_link"] = promote.promo_edit_url(None, id36=link_id36) - self.promos = sorted(promos.values(), + self.promos = sorted(promos.values(), key=lambda x: (x['username'], x['campaign_start'])) Reddit.__init__(self, title="Promo Admin Tool", show_sidebar=False) @@ -3340,7 +3340,7 @@ class PromoAdminTool(Reddit): def get_promo_info(self, start_date, end_date): promo_info = {} - scheduled = Promote_Graph.get_current_promos(start_date, + scheduled = Promote_Graph.get_current_promos(start_date, end_date + datetime.timedelta(1)) campaign_ids = [x[1] for x in scheduled] campaigns = PromoCampaign._byID(campaign_ids, data=True, return_dict=True) @@ -3351,7 +3351,7 @@ class PromoAdminTool(Reddit): days = (campaign.end_date - campaign.start_date).days bid_per_day = float(campaign.bid) / days account = accounts[campaign.owner_id] - promo_info[campaign._id] = { + promo_info[campaign._id] = { 'username': account.name, 'user_email': account.email, 'link_title': link.title, @@ -3359,8 +3359,8 @@ class PromoAdminTool(Reddit): 'campaign_start': campaign.start_date.strftime("%Y/%m/%d"), 'campaign_end': campaign.end_date.strftime("%Y/%m/%d"), 'bid_per_day': bid_per_day, - } - return promo_info + } + return promo_info @@ -3442,7 +3442,7 @@ def make_link_child(item): editable = (expand and item.author == c.user and - not item._deleted) + not item._deleted) link_child = SelfTextChild(item, expand = expand, nofollow = item.nofollow) @@ -3503,7 +3503,7 @@ class UserText(CachedTemplate): text = '' CachedTemplate.__init__(self, - fullname = item._fullname if item else "", + fullname = item._fullname if item else "", text = text, have_form = have_form, editable = editable, @@ -3555,7 +3555,7 @@ class Promotion_Summary(Templated): campaign_bid = campaign.bid except AttributeError, e: g.log.error("Corrupt PromoCampaign (link: %d, camp_id, %d) " - "omitted from promotion summary. Error was: %r" % + "omitted from promotion summary. Error was: %r" % (link._id, camp_id, e)) continue @@ -3563,17 +3563,17 @@ class Promotion_Summary(Templated): links.add(link) link.bid = getattr(link, "bid", 0) + campaign_bid link.ncampaigns = getattr(link, "ncampaigns", 0) + 1 - + bid_per_day = campaign_bid / (campaign_end_date - campaign_start_date).days sd = max(start_date, campaign_start_date.date()) ed = min(end_date, campaign_end_date.date()) - + self.total += bid_per_day * (ed - sd).days - + authors.setdefault(link.author.name, []).append(link) author_score[link.author.name] = author_score.get(link.author.name, 0) + link._score - + links = list(links) links.sort(key = lambda x: x._score, reverse = True) author_score = list(sorted(((v, k) for k,v in author_score.iteritems()), @@ -3599,7 +3599,7 @@ def force_datetime(d): class Promote_Graph(Templated): - + @classmethod @memoize('get_market', time = 60) def get_market(cls, user_id, start_date, end_date): @@ -3608,7 +3608,7 @@ class Promote_Graph(Templated): def callback(link, bid_day, starti, endi, campaign): for i in xrange(starti, endi): if user_id is None or link.author_id == user_id: - if (not promote.is_unpaid(link) and + if (not promote.is_unpaid(link) and not promote.is_rejected(link) and campaign.trans_id != NO_TRANSACTION): market[i] = market.get(i, 0) + bid_day @@ -3631,7 +3631,7 @@ class Promote_Graph(Templated): endi = min((edate - start_date).days, size) bid_day = campaign.bid / max((edate - sdate).days, 1) callback(link, bid_day, starti, endi, campaign) - + @classmethod def get_current_promos(cls, start_date, end_date): # grab promoted links @@ -3646,7 +3646,7 @@ class Promote_Graph(Templated): links = dict((l._fullname, l) for l in links.things if promote.is_accepted(l) or promote.is_unapproved(l)) # filter promos accordingly - promos = [(links[thing_name], campaign_id, s, e) + promos = [(links[thing_name], campaign_id, s, e) for thing_name, campaign_id, s, e in promos if links.has_key(thing_name)] @@ -3787,7 +3787,7 @@ class Promote_Graph(Templated): for link, uimp, nimp, ucli, ncli in self.recent: yield (link._date.strftime("%Y-%m-%d"), num(uimp), num(nimp), num(ucli), num(ncli), - num(link._ups - link._downs), + num(link._ups - link._downs), "$%.2f" % link.promote_bid, _force_unicode(link.title)) diff --git a/r2/r2/lib/utils/utils.py b/r2/r2/lib/utils/utils.py index a9f54ea78..8fe52ac50 100644 --- a/r2/r2/lib/utils/utils.py +++ b/r2/r2/lib/utils/utils.py @@ -47,9 +47,9 @@ from mako.filters import url_escape from r2.lib.contrib import ipaddress from r2.lib.require import require, require_split import snudown - + from r2.lib.utils._utils import * - + iters = (list, tuple, set) def randstr(len, reallyrandom = False): @@ -67,7 +67,7 @@ class Storage(dict): """ A Storage object is like a dictionary except `obj.foo` can be used in addition to `obj['foo']`. - + >>> o = storage(a=1) >>> o.a 1 @@ -81,15 +81,15 @@ class Storage(dict): Traceback (most recent call last): ... AttributeError: 'a' - + """ - def __getattr__(self, key): + def __getattr__(self, key): try: return self[key] except KeyError, k: raise AttributeError, k - def __setattr__(self, key, value): + def __setattr__(self, key, value): self[key] = value def __delattr__(self, key): @@ -98,7 +98,7 @@ class Storage(dict): except KeyError, k: raise AttributeError, k - def __repr__(self): + def __repr__(self): return '' storage = Storage @@ -106,16 +106,16 @@ storage = Storage def storify(mapping, *requireds, **defaults): """ Creates a `storage` object from dictionary `mapping`, raising `KeyError` if - d doesn't have all of the keys in `requireds` and using the default + d doesn't have all of the keys in `requireds` and using the default values for keys found in `defaults`. For example, `storify({'a':1, 'c':3}, b=2, c=0)` will return the equivalent of `storage({'a':1, 'b':2, 'c':3})`. - - If a `storify` value is a list (e.g. multiple values in a form submission), - `storify` returns the last element of the list, unless the key appears in + + If a `storify` value is a list (e.g. multiple values in a form submission), + `storify` returns the last element of the list, unless the key appears in `defaults` as a list. Thus: - + >>> storify({'a':[1, 2]}).a 2 >>> storify({'a':[1, 2]}, a=[]).a @@ -124,24 +124,24 @@ def storify(mapping, *requireds, **defaults): [1] >>> storify({}, a=[]).a [] - + Similarly, if the value has a `value` attribute, `storify will return _its_ value, unless the key appears in `defaults` as a dictionary. - + >>> storify({'a':storage(value=1)}).a 1 >>> storify({'a':storage(value=1)}, a={}).a >>> storify({}, a={}).a {} - + """ def getvalue(x): if hasattr(x, 'value'): return x.value else: return x - + stor = Storage() for key in requireds + tuple(mapping.keys()): value = mapping[key] @@ -158,12 +158,12 @@ def storify(mapping, *requireds, **defaults): for (key, value) in defaults.iteritems(): result = value - if hasattr(stor, key): + if hasattr(stor, key): result = stor[key] - if value == () and not isinstance(result, tuple): + if value == () and not isinstance(result, tuple): result = (result,) setattr(stor, key, result) - + return stor class Enum(Storage): @@ -175,7 +175,7 @@ class Enum(Storage): return item in self.values() else: return Storage.__contains__(self, item) - + class Results(): def __init__(self, sa_ResultProxy, build_fn, do_batch=False): @@ -289,7 +289,7 @@ def extract_title(data): bs = BeautifulSoup(data, convertEntities=BeautifulSoup.HTML_ENTITIES) if not bs: return - + title_bs = bs.html.head.title if not title_bs or not title_bs.string: @@ -314,7 +314,7 @@ def extract_title(data): title = re.sub(r'\s+', ' ', title, flags=re.UNICODE) return title.encode('utf-8').strip() - + valid_schemes = ('http', 'https', 'ftp', 'mailto') valid_dns = re.compile('\A[-a-zA-Z0-9]+\Z') def sanitize_url(url, require_scheme = False): @@ -509,7 +509,7 @@ class UrlParser(object): q.update(self._url_updates) q = query_string(q).lstrip('?') - # make sure the port is not doubly specified + # make sure the port is not doubly specified if self.port and ":" in self.hostname: self.hostname = self.hostname.split(':')[0] @@ -600,7 +600,7 @@ class UrlParser(object): if require_frame and not self.query_dict.has_key(self.cname_get): return self - # fetch the subreddit and make sure it + # fetch the subreddit and make sure it subreddit = subreddit or self.get_subreddit() if subreddit and subreddit.domain: @@ -756,7 +756,7 @@ def fetch_things(t_class,since,until,batch_fn=None, q = t_class._query(*query_params, **query_dict) - + orig_rules = deepcopy(q._rules) things = list(q) @@ -914,7 +914,7 @@ def valid_hash(user, hash): def check_cheating(loc): pass - + def vote_hash(user, thing, note='valid'): return user.name @@ -1383,7 +1383,7 @@ def thread_dump(*a): def constant_time_compare(actual, expected): """ Returns True if the two strings are equal, False otherwise - + The time taken is dependent on the number of characters provided instead of the number of characters that match. """ diff --git a/r2/r2/models/vote.py b/r2/r2/models/vote.py index 77d6d017c..79542c4a8 100644 --- a/r2/r2/models/vote.py +++ b/r2/r2/models/vote.py @@ -188,7 +188,7 @@ class Vote(MultiRelation('vote', old_valid_thing = getattr(v, 'valid_thing', False) v.valid_thing = (valid_thing(v, karma, cheater = cheater) and getattr(v,'valid_thing', False)) - v.valid_user = (getattr(v, 'valid_user', False) + v.valid_user = (getattr(v, 'valid_user', False) and v.valid_thing and valid_user(v, sr, karma)) #new vote