Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Round 2 of import updates

* Added @export decorator
* Fixed @memoize and @wraps_api to use functools @wraps to ensure __name__ and
  __module__ are preserved
* refactored a few files to handle cycles
* added test_imports.py to allow 'compiling' all the python files and ensure
  they
* r2.config up to import snuff
* r2.controllers up to import snuff
* removed large import blocks and replaced with use of <module>.<symbol> style
* Fixed a lot of kw args with a key = value style to use key=value (only fixed
  where other change was needed)
* Started work on r2.lib, but not finished yet
  • Loading branch information...
commit 25155c2f4bf41fba56aad2a65e63592f5a1db90f 1 parent 235f5b7
Kevin Kress authored

Showing 81 changed files with 3,255 additions and 2,704 deletions. Show diff stats Hide diff stats

  1. +5 0 r2/r2/config/environment.py
  2. +17 0 r2/r2/config/extensions.py
  3. +8 4 r2/r2/config/middleware.py
  4. +6 0 r2/r2/config/rewrites.py
  5. +1 2  r2/r2/config/routing.py
  6. +27 27 r2/r2/config/templates.py
  7. +52 38 r2/r2/controllers/__init__.py
  8. +14 9 r2/r2/controllers/ads.py
  9. +610 659 r2/r2/controllers/api.py
  10. +14 3 r2/r2/controllers/api_docs.py
  11. +5 0 r2/r2/controllers/apiv1.py
  12. +24 26 r2/r2/controllers/awards.py
  13. +36 40 r2/r2/controllers/buttons.py
  14. +5 0 r2/r2/controllers/captcha.py
  15. +5 0 r2/r2/controllers/embed.py
  16. +5 0 r2/r2/controllers/error.py
  17. +13 7 r2/r2/controllers/errorlog.py
  18. +5 0 r2/r2/controllers/errors.py
  19. +12 7 r2/r2/controllers/feedback.py
  20. +377 442 r2/r2/controllers/front.py
  21. +5 0 r2/r2/controllers/health.py
  22. +26 26 r2/r2/controllers/ipn.py
  23. +125 153 r2/r2/controllers/listingcontroller.py
  24. +5 1 r2/r2/controllers/mediaembed.py
  25. +45 37 r2/r2/controllers/oauth2.py
  26. +59 56 r2/r2/controllers/post.py
  27. +136 152 r2/r2/controllers/promotecontroller.py
  28. +5 1 r2/r2/controllers/querycontroller.py
  29. +48 65 r2/r2/controllers/reddit_base.py
  30. +5 0 r2/r2/controllers/redirect.py
  31. +10 3 r2/r2/controllers/toolbar.py
  32. +187 1 r2/r2/controllers/validator/validator.py
  33. +20 9 r2/r2/lib/amqp.py
  34. +61 43 r2/r2/lib/app_globals.py
  35. +3 1 r2/r2/lib/authentication.py
  36. +1 0  r2/r2/lib/authorize/__init__.py
  37. +11 7 r2/r2/lib/authorize/api.py
  38. +5 2 r2/r2/lib/authorize/interaction.py
  39. +23 7 r2/r2/lib/cloudsearch.py
  40. +1 1  r2/r2/lib/cssfilter.py
  41. +41 0 r2/r2/lib/export.py
  42. +12 8 r2/r2/lib/jsonresponse.py
  43. +2 1  r2/r2/lib/memoize.py
  44. +13 6 r2/r2/lib/menus.py
  45. +6 5 r2/r2/lib/mr_account.py
  46. +3 1 r2/r2/lib/pages/__init__.py
  47. +16 3 r2/r2/lib/pages/admin_pages.py
  48. +439 291 r2/r2/lib/pages/pages.py
  49. +9 40 r2/r2/lib/pages/things.py
  50. +11 7 r2/r2/lib/pages/trafficpages.py
  51. +2 2 r2/r2/lib/wrapped.pyx
  52. +76 0 r2/r2/lib/wrapper.py
  53. +35 23 r2/r2/models/account.py
  54. +3 5 r2/r2/models/account_subreddit.py
  55. +9 2 r2/r2/models/ad.py
  56. +38 25 r2/r2/models/admintools.py
  57. +5 8 r2/r2/models/award.py
  58. +76 80 r2/r2/models/bidding.py
  59. +8 10 r2/r2/models/builder.py
  60. +22 16 r2/r2/models/builders.py
  61. +6 9 r2/r2/models/flair.py
  62. +52 50 r2/r2/models/gold.py
  63. +6 8 r2/r2/models/jury.py
  64. +4 5 r2/r2/models/keyvalue.py
  65. +3 5 r2/r2/models/last_modified.py
  66. +37 26 r2/r2/models/link.py
  67. +11 9 r2/r2/models/listing.py
  68. +109 110 r2/r2/models/mail_queue.py
  69. +3 7 r2/r2/models/modaction.py
  70. +4 2 r2/r2/models/populatedb.py
  71. +3 5 r2/r2/models/printable.py
  72. +4 5 r2/r2/models/promo.py
  73. +12 14 r2/r2/models/query_cache.py
  74. +3 7 r2/r2/models/report.py
  75. +37 29 r2/r2/models/subreddit.py
  76. +10 12 r2/r2/models/token.py
  77. +13 16 r2/r2/models/traffic.py
  78. +3 7 r2/r2/models/trial.py
  79. +17 15 r2/r2/models/vote.py
  80. +69 0 r2/r2/tests/functional/test_imports.py
  81. +1 1  r2/r2/tests/test_models.py
5 r2/r2/config/environment.py
@@ -30,11 +30,16 @@
30 30 from r2.config import routing
31 31 from r2.lib.app_globals import Globals
32 32 from r2.lib.configparse import ConfigValue
  33 +from r2.lib.export import export
33 34
  35 +__all__ = [
  36 + #Constants Only, use @export for functions/classes
  37 + ]
34 38
35 39 mimetypes.init()
36 40
37 41
  42 +@export
38 43 def load_environment(global_conf={}, app_conf={}, setup_globals=True):
39 44 # Setup our paths
40 45 root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 r2/r2/config/extensions.py
@@ -22,16 +22,31 @@
22 22
23 23 from pylons import c
24 24
  25 +from r2.lib.export import export
  26 +
  27 +__all__ = [
  28 + #Constants Only, use @export for functions/classes
  29 + "API_TYPES",
  30 + "extension_mapping",
  31 + ]
  32 +
  33 +
  34 +@export
25 35 def api_type(subtype = ''):
26 36 return 'api-' + subtype if subtype else 'api'
27 37
  38 +
  39 +@export
28 40 def is_api(subtype = ''):
29 41 return c.render_style and c.render_style.startswith(api_type(subtype))
30 42
  43 +
  44 +@export
31 45 def get_api_subtype():
32 46 if is_api() and c.render_style.startswith('api-'):
33 47 return c.render_style[4:]
34 48
  49 +
35 50 extension_mapping = {
36 51 "rss": ("xml", "text/xml; charset=UTF-8"),
37 52 "xml": ("xml", "text/xml; charset=UTF-8"),
@@ -50,8 +65,10 @@ def get_api_subtype():
50 65 "i": ("compact", "text/html; charset=UTF-8"),
51 66 }
52 67
  68 +
53 69 API_TYPES = ('api', 'json')
54 70
  71 +@export
55 72 def set_extension(environ, ext):
56 73 environ["extension"] = ext
57 74 environ["render_style"], environ["content_type"] = extension_mapping[ext]
12 r2/r2/config/middleware.py
@@ -24,22 +24,26 @@
24 24 import re
25 25 import urllib
26 26 import tempfile
27   -import urlparse
28 27 from threading import Lock
29 28
30 29 from paste.cascade import Cascade
  30 +from paste.deploy.converters import asbool
31 31 from paste.registry import RegistryManager
32 32 from paste.urlparser import StaticURLParser
33   -from paste.deploy.converters import asbool
34 33 from pylons import config, Response
35 34 from pylons.error import error_template
36 35 from pylons.middleware import ErrorDocuments, ErrorHandler, StaticJavascripts
37 36 from pylons.wsgiapp import PylonsApp, PylonsBaseWSGIApp
38 37
  38 +from r2.lib.utils import is_subdomain
  39 +
39 40 from r2.config.environment import load_environment
40   -from r2.config.rewrites import rewrites
41 41 from r2.config.extensions import extension_mapping, set_extension
42   -from r2.lib.utils import is_subdomain
  42 +from r2.config.rewrites import rewrites
  43 +
  44 +__all__ = [
  45 + #Constants Only, use @export for functions/classes
  46 + ]
43 47
44 48
45 49 # hack in Paste support for HTTP 429 "Too Many Requests"
6 r2/r2/config/rewrites.py
@@ -22,6 +22,12 @@
22 22
23 23 import re
24 24
  25 +__all__ = [
  26 + #Constants Only, use @export for functions/classes
  27 + "rewrites",
  28 + ]
  29 +
  30 +
25 31 rewrites = (#these first two rules prevent the .embed rewrite from
26 32 #breaking other js that should work
27 33 ("\A/_(.*)", "/_$1"),
3  r2/r2/config/routing.py
@@ -23,9 +23,8 @@
23 23 """
24 24 Setup your Routes options here
25 25 """
26   -import os
27   -from routes import Mapper
28 26 from pylons import config
  27 +from routes import Mapper
29 28
30 29 def make_map():
31 30 map = Mapper()
54 r2/r2/config/templates.py
@@ -20,8 +20,8 @@
20 20 # Inc. All Rights Reserved.
21 21 ###############################################################################
22 22
  23 +import r2.lib.jsontemplates as jtmpl
23 24 from r2.lib.manager import tp_manager
24   -from r2.lib.jsontemplates import *
25 25
26 26 tpm = tp_manager.tp_manager()
27 27
@@ -31,35 +31,35 @@ def api(type, cls):
31 31 tpm.add_handler(type, 'api-compact', cls())
32 32
33 33 # blanket fallback rule
34   -api('templated', NullJsonTemplate)
  34 +api('templated', jtmpl.NullJsonTemplate)
35 35
36 36 # class specific overrides
37   -api('link', LinkJsonTemplate)
38   -api('promotedlink', PromotedLinkJsonTemplate)
39   -api('comment', CommentJsonTemplate)
40   -api('message', MessageJsonTemplate)
41   -api('subreddit', SubredditJsonTemplate)
42   -api('morerecursion', MoreCommentJsonTemplate)
43   -api('morechildren', MoreCommentJsonTemplate)
44   -api('reddit', RedditJsonTemplate)
45   -api('panestack', PanestackJsonTemplate)
46   -api('listing', ListingJsonTemplate)
47   -api('modlist', UserListJsonTemplate)
48   -api('userlist', UserListJsonTemplate)
49   -api('contributorlist', UserListJsonTemplate)
50   -api('bannedlist', UserListJsonTemplate)
51   -api('friendlist', UserListJsonTemplate)
52   -api('usertableitem', UserTableItemJsonTemplate)
53   -api('account', AccountJsonTemplate)
  37 +api('link', jtmpl.LinkJsonTemplate)
  38 +api('promotedlink', jtmpl.PromotedLinkJsonTemplate)
  39 +api('comment', jtmpl.CommentJsonTemplate)
  40 +api('message', jtmpl.MessageJsonTemplate)
  41 +api('subreddit', jtmpl.SubredditJsonTemplate)
  42 +api('morerecursion', jtmpl.MoreCommentJsonTemplate)
  43 +api('morechildren', jtmpl.MoreCommentJsonTemplate)
  44 +api('reddit', jtmpl.RedditJsonTemplate)
  45 +api('panestack', jtmpl.PanestackJsonTemplate)
  46 +api('listing', jtmpl.ListingJsonTemplate)
  47 +api('modlist', jtmpl.UserListJsonTemplate)
  48 +api('userlist', jtmpl.UserListJsonTemplate)
  49 +api('contributorlist', jtmpl.UserListJsonTemplate)
  50 +api('bannedlist', jtmpl.UserListJsonTemplate)
  51 +api('friendlist', jtmpl.UserListJsonTemplate)
  52 +api('usertableitem', jtmpl.UserTableItemJsonTemplate)
  53 +api('account', jtmpl.AccountJsonTemplate)
54 54
55   -api('organiclisting', OrganicListingJsonTemplate)
56   -api('subreddittraffic', TrafficJsonTemplate)
57   -api('takedownpane', TakedownJsonTemplate)
  55 +api('organiclisting', jtmpl.OrganicListingJsonTemplate)
  56 +api('subreddittraffic', jtmpl.TrafficJsonTemplate)
  57 +api('takedownpane', jtmpl.TakedownJsonTemplate)
58 58
59   -api('flairlist', FlairListJsonTemplate)
60   -api('flaircsv', FlairCsvJsonTemplate)
  59 +api('flairlist', jtmpl.FlairListJsonTemplate)
  60 +api('flaircsv', jtmpl.FlairCsvJsonTemplate)
61 61
62   -api('subredditstylesheet', StylesheetTemplate)
63   -api('createsubreddit', SubredditSettingsTemplate)
  62 +api('subredditstylesheet', jtmpl.StylesheetTemplate)
  63 +api('createsubreddit', jtmpl.SubredditSettingsTemplate)
64 64
65   -tpm.add_handler('usertableitem', 'api-html', UserItemHTMLJsonTemplate())
  65 +tpm.add_handler('usertableitem', 'api-html', jtmpl.UserItemHTMLJsonTemplate())
90 r2/r2/controllers/__init__.py
@@ -20,9 +20,18 @@
20 20 # Inc. All Rights Reserved.
21 21 ###############################################################################
22 22
  23 +from r2.lib.export import export
  24 +
  25 +__all__ = [
  26 + #Constants Only, use @export for functions/classes
  27 + ]
  28 +
  29 +
23 30 _reddit_controllers = {}
24 31 _plugin_controllers = {}
25 32
  33 +
  34 +@export
26 35 def get_controller(name):
27 36 name = name.lower() + 'controller'
28 37 if name in _reddit_controllers:
@@ -32,53 +41,58 @@ def get_controller(name):
32 41 else:
33 42 raise KeyError(name)
34 43
  44 +
  45 +@export
35 46 def add_controller(controller):
36 47 name = controller.__name__.lower()
37 48 assert name not in _plugin_controllers
38 49 _plugin_controllers[name] = controller
39 50 return controller
40 51
  52 +
  53 +@export
41 54 def load_controllers():
42   - from listingcontroller import ListingController
43   - from listingcontroller import HotController
44   - from listingcontroller import NewController
45   - from listingcontroller import BrowseController
46   - from listingcontroller import MessageController
47   - from listingcontroller import RedditsController
48   - from listingcontroller import ByIDController
49   - from listingcontroller import RandomrisingController
50   - from listingcontroller import UserController
51   - from listingcontroller import CommentsController
  55 + from r2.controllers.listingcontroller import ListingController
  56 + from r2.controllers.listingcontroller import HotController
  57 + from r2.controllers.listingcontroller import NewController
  58 + from r2.controllers.listingcontroller import BrowseController
  59 + from r2.controllers.listingcontroller import MessageController
  60 + from r2.controllers.listingcontroller import RedditsController
  61 + from r2.controllers.listingcontroller import ByIDController
  62 + from r2.controllers.listingcontroller import RandomrisingController
  63 + from r2.controllers.listingcontroller import UserController
  64 + from r2.controllers.listingcontroller import CommentsController
52 65
53   - from listingcontroller import MyredditsController
  66 + from r2.controllers.listingcontroller import MyredditsController
54 67
55   - from feedback import FeedbackController
56   - from front import FormsController
57   - from front import FrontController
58   - from health import HealthController
59   - from buttons import ButtonsController
60   - from captcha import CaptchaController
61   - from embed import EmbedController
62   - from error import ErrorController
63   - from post import PostController
64   - from toolbar import ToolbarController
65   - from awards import AwardsController
66   - from ads import AdsController
67   - from usage import UsageController
68   - from errorlog import ErrorlogController
69   - from promotecontroller import PromoteController
70   - from mediaembed import MediaembedController
71   - from mediaembed import AdController
  68 + from r2.controllers.feedback import FeedbackController
  69 + from r2.controllers.front import FormsController
  70 + from r2.controllers.front import FrontController
  71 + from r2.controllers.health import HealthController
  72 + from r2.controllers.buttons import ButtonsController
  73 + from r2.controllers.captcha import CaptchaController
  74 + from r2.controllers.embed import EmbedController
  75 + from r2.controllers.error import ErrorController
  76 + from r2.controllers.post import PostController
  77 + from r2.controllers.toolbar import ToolbarController
  78 + from r2.controllers.awards import AwardsController
  79 + from r2.controllers.ads import AdsController
  80 + from r2.controllers.usage import UsageController
  81 + from r2.controllers.errorlog import ErrorlogController
  82 + from r2.controllers.promotecontroller import PromoteController
  83 + from r2.controllers.mediaembed import MediaembedController
  84 + from r2.controllers.mediaembed import AdController
72 85
73   - from querycontroller import QueryController
  86 + from r2.controllers.querycontroller import QueryController
74 87
75   - from api import ApiController
76   - from api import ApiminimalController
77   - from api_docs import ApidocsController
78   - from apiv1 import APIv1Controller
79   - from oauth2 import OAuth2FrontendController
80   - from oauth2 import OAuth2AccessController
81   - from redirect import RedirectController
82   - from ipn import IpnController
  88 + from r2.controllers.api import ApiController
  89 + from r2.controllers.api import ApiminimalController
  90 + from r2.controllers.api_docs import ApidocsController
  91 + from r2.controllers.apiv1 import APIv1Controller
  92 + from r2.controllers.oauth2 import OAuth2FrontendController
  93 + from r2.controllers.oauth2 import OAuth2AccessController
  94 + from r2.controllers.redirect import RedirectController
  95 + from r2.controllers.ipn import IpnController
83 96
84   - _reddit_controllers.update((name.lower(), obj) for name, obj in locals().iteritems())
  97 + controllers = [(name.lower(), obj) for name, obj in locals().iteritems()]
  98 + _reddit_controllers.update(controllers)
23 r2/r2/controllers/ads.py
@@ -22,25 +22,30 @@
22 22
23 23 from pylons.controllers.util import abort
24 24
  25 +from r2.lib.export import export
25 26 from r2.lib.pages import AdminPage, AdminAds, AdminAdAssign, AdminAdSRs
26 27
  28 +import r2.controllers.validator as validator
27 29 from r2.controllers.reddit_base import RedditController
28   -from r2.controllers.validator import (VAdByCodename,
29   - VSponsorAdmin,
30   - validate,
31   - )
  30 +from r2.controllers.validator import validate
32 31
  32 +__all__ = [
  33 + #Constants Only, use @export for functions/classes
  34 + ]
  35 +
  36 +
  37 +@export
33 38 class AdsController(RedditController):
34 39
35   - @validate(VSponsorAdmin())
  40 + @validate(validator.VSponsorAdmin())
36 41 def GET_index(self):
37 42 res = AdminPage(content = AdminAds(),
38 43 show_sidebar = False,
39 44 title = 'ads').render()
40 45 return res
41 46
42   - @validate(VSponsorAdmin(),
43   - ad = VAdByCodename('adcn'))
  47 + @validate(validator.VSponsorAdmin(),
  48 + ad = validator.VAdByCodename('adcn'))
44 49 def GET_assign(self, ad):
45 50 if ad is None:
46 51 abort(404, 'page not found')
@@ -50,8 +55,8 @@ def GET_assign(self, ad):
50 55 title='assign an ad to a community').render()
51 56 return res
52 57
53   - @validate(VSponsorAdmin(),
54   - ad = VAdByCodename('adcn'))
  58 + @validate(validator.VSponsorAdmin(),
  59 + ad = validator.VAdByCodename('adcn'))
55 60 def GET_srs(self, ad):
56 61 if ad is None:
57 62 abort(404, 'page not found')
1,269 r2/r2/controllers/api.py
610 additions, 659 deletions not shown
17 r2/r2/controllers/api_docs.py
@@ -21,16 +21,24 @@
21 21 ###############################################################################
22 22
23 23 import re
  24 +import inspect
24 25 from collections import defaultdict
25 26 from itertools import chain
26   -import inspect
27 27 from os.path import abspath, relpath
28 28
29 29 from pylons import g
30 30 from pylons.i18n import _
31   -from reddit_base import RedditController
32   -from r2.lib.utils import Storage
  31 +
  32 +from r2.lib.export import export
33 33 from r2.lib.pages import BoringPage, ApiHelp
  34 +from r2.lib.utils import Storage
  35 +
  36 +from r2.controllers.reddit_base import RedditController
  37 +
  38 +__all__ = [
  39 + #Constants Only, use @export for functions/classes
  40 + ]
  41 +
34 42
35 43 # API sections displayed in the documentation page.
36 44 # Each section can have a title and a markdown-formatted description.
@@ -72,6 +80,8 @@
72 80
73 81 api_section = Storage((k, k) for k in section_info)
74 82
  83 +
  84 +@export
75 85 def api_doc(section, **kwargs):
76 86 """
77 87 Add documentation annotations to the decorated function.
@@ -94,6 +104,7 @@ def add_metadata(api_function):
94 104 return api_function
95 105 return add_metadata
96 106
  107 +
97 108 class ApidocsController(RedditController):
98 109 @staticmethod
99 110 def docs_from_controller(controller, url_prefix='/api'):
5 r2/r2/controllers/apiv1.py
@@ -27,6 +27,11 @@
27 27 from r2.controllers.api_docs import api_doc, api_section
28 28 from r2.controllers.oauth2 import OAuth2ResourceController, require_oauth2_scope
29 29
  30 +__all__ = [
  31 + #Constants Only, use @export for functions/classes
  32 + ]
  33 +
  34 +
30 35 class APIv1Controller(OAuth2ResourceController):
31 36 def try_pagecache(self):
32 37 pass
50 r2/r2/controllers/awards.py
@@ -22,48 +22,46 @@
22 22
23 23 from pylons import request, g
24 24
25   -from r2.lib.pages import (AdminPage,
26   - AdminAwards,
27   - AdminAwardGive,
28   - AdminAwardWinners,
29   - )
  25 +from r2.lib import pages
30 26
  27 +from r2.controllers import validator
31 28 from r2.controllers.reddit_base import RedditController
32   -from r2.controllers.validator import (VAdmin,
33   - VAwardByCodename,
34   - nop,
35   - validate,
36   - )
  29 +from r2.controllers.validator import validate
  30 +
  31 +__all__ = [
  32 + #Constants Only, use @export for functions/classes
  33 + ]
  34 +
37 35
38 36 class AwardsController(RedditController):
39 37
40   - @validate(VAdmin())
  38 + @validate(validator.VAdmin())
41 39 def GET_index(self):
42   - res = AdminPage(content = AdminAwards(),
43   - title = 'awards').render()
  40 + res = pages.AdminPage(content = pages.AdminAwards(),
  41 + title = 'awards').render()
44 42 return res
45 43
46   - @validate(VAdmin(),
47   - award = VAwardByCodename('awardcn'),
48   - recipient = nop('recipient'),
49   - desc = nop('desc'),
50   - url = nop('url'),
51   - hours = nop('hours'))
  44 + @validate(validator.VAdmin(),
  45 + award=validator.VAwardByCodename('awardcn'),
  46 + recipient=validator.nop('recipient'),
  47 + desc=validator.nop('desc'),
  48 + url=validator.nop('url'),
  49 + hours=validator.nop('hours'))
52 50 def GET_give(self, award, recipient, desc, url, hours):
53 51 if award is None:
54 52 abort(404, 'page not found')
55 53
56   - res = AdminPage(content = AdminAwardGive(award, recipient, desc,
57   - url, hours),
58   - title='give an award').render()
  54 + res = pages.AdminPage(content = pages.AdminAwardGive(award, recipient,
  55 + desc, url, hours),
  56 + title='give an award').render()
59 57 return res
60 58
61   - @validate(VAdmin(),
62   - award = VAwardByCodename('awardcn'))
  59 + @validate(validator.VAdmin(),
  60 + award=validator.VAwardByCodename('awardcn'))
63 61 def GET_winners(self, award):
64 62 if award is None:
65 63 abort(404, 'page not found')
66 64
67   - res = AdminPage(content = AdminAwardWinners(award),
68   - title='award winners').render()
  65 + res = pages.AdminPage(content = pages.AdminAwardWinners(award),
  66 + title='award winners').render()
69 67 return res
76 r2/r2/controllers/buttons.py
@@ -24,27 +24,20 @@
24 24 from pylons.controllers.util import abort
25 25 from pylons.i18n import _
26 26
27   -from r2.lib.pages import (Bookmarklets,
28   - BoringPage,
29   - ButtonDemoPanel,
30   - ButtonLite,
31   - WidgetDemoPanel,
32   - )
33   -from r2.lib.pages.things import wrap_links, NotFound
  27 +import r2.models as models
  28 +from r2.lib import pages
  29 +from r2.lib.db.thing import NotFound
34 30 from r2.lib.utils import tup
35   -from r2.models import (DomainSR,
36   - FakeSubreddit,
37   - Link,
38   - )
  31 +from r2.lib.wrapper import wrap_links
39 32
40   -from r2.controllers.validator import (VBoolean,
41   - VInt,
42   - VSanitizedUrl,
43   - nop,
44   - validate,
45   - )
  33 +import r2.controllers.validator as validator
  34 +from r2.controllers.validator import validate
46 35
47   -from reddit_base import RedditController
  36 +from r2.controllers.reddit_base import RedditController
  37 +
  38 +__all__ = [
  39 + #Constants Only, use @export for functions/classes
  40 + ]
48 41
49 42 class ButtonsController(RedditController):
50 43 def get_wrapped_link(self, url, link = None, wrapper = None):
@@ -53,9 +46,11 @@ def get_wrapped_link(self, url, link = None, wrapper = None):
53 46 if link:
54 47 links = [link]
55 48 else:
56   - sr = None if isinstance(c.site, FakeSubreddit) else c.site
  49 + sr = c.site
  50 + if isinstance(c.site, models.FakeSubreddit):
  51 + sr = None
57 52 try:
58   - links = tup(Link._by_url(url, sr))
  53 + links = tup(models.Link._by_url(url, sr))
59 54 except NotFound:
60 55 pass
61 56
@@ -82,7 +77,7 @@ def get_wrapped_link(self, url, link = None, wrapper = None):
82 77 if wrapper:
83 78 return wrapper(None)
84 79
85   - @validate(buttontype = VInt('t', 1, 5))
  80 + @validate(buttontype=validator.VInt('t', 1, 5))
86 81 def GET_button_embed(self, buttontype):
87 82 if not buttontype:
88 83 abort(404)
@@ -90,11 +85,11 @@ def GET_button_embed(self, buttontype):
90 85 return self.redirect('/static/button/button%s.js' % buttontype,
91 86 code=301)
92 87
93   - @validate(buttonimage = VInt('i', 0, 14),
94   - title = nop('title'),
95   - url = VSanitizedUrl('url'),
96   - newwindow = VBoolean('newwindow', default = False),
97   - styled = VBoolean('styled', default=True))
  88 + @validate(buttonimage=validator.VInt('i', 0, 14),
  89 + title=validator.nop('title'),
  90 + url=validator.VSanitizedUrl('url'),
  91 + newwindow=validator.VBoolean('newwindow', default=False),
  92 + styled=validator.VBoolean('styled', default=True))
98 93 def GET_button_lite(self, buttonimage, title, url, styled, newwindow):
99 94 c.render_style = 'js'
100 95 c.response_content_type = 'text/javascript; charset=UTF-8'
@@ -109,29 +104,30 @@ def builder_wrapper(thing = None):
109 104 if not thing:
110 105 kw['url'] = url
111 106 kw['title'] = title
112   - return ButtonLite(thing,
113   - image = 1 if buttonimage is None else buttonimage,
114   - target = "_new" if newwindow else "_parent",
115   - styled = styled, **kw)
  107 +
  108 + image = 1 if buttonimage is None else buttonimage
  109 + target = "_new" if newwindow else "_parent",
  110 + return pages.ButtonLite(thing, image=image, target=target,
  111 + styled=styled, **kw)
116 112
117 113 bjs = self.get_wrapped_link(url, wrapper = builder_wrapper)
118 114 return self.sendjs(bjs.render(), callback='', escape=False)
119 115
120 116 def GET_button_demo_page(self):
121 117 # no buttons for domain listings -> redirect to top level
122   - if isinstance(c.site, DomainSR):
  118 + if isinstance(c.site, models.DomainSR):
123 119 return self.redirect('/buttons')
124   - return BoringPage(_("reddit buttons"),
125   - show_sidebar = False,
126   - content=ButtonDemoPanel()).render()
  120 + return pages.BoringPage(_("reddit buttons"),
  121 + show_sidebar=False,
  122 + content=pages.ButtonDemoPanel()).render()
127 123
128 124 def GET_widget_demo_page(self):
129   - return BoringPage(_("reddit widget"),
130   - show_sidebar = False,
131   - content=WidgetDemoPanel()).render()
  125 + return pages.BoringPage(_("reddit widget"),
  126 + show_sidebar=False,
  127 + content=pages.WidgetDemoPanel()).render()
132 128
133 129 def GET_bookmarklets(self):
134   - return BoringPage(_("bookmarklets"),
135   - show_sidebar = False,
136   - content=Bookmarklets()).render()
  130 + return pages.BoringPage(_("bookmarklets"),
  131 + show_sidebar=False,
  132 + content=pages.Bookmarklets()).render()
137 133
5 r2/r2/controllers/captcha.py
@@ -26,6 +26,11 @@
26 26
27 27 from r2.controllers.reddit_base import RedditController
28 28
  29 +__all__ = [
  30 + #Constants Only, use @export for functions/classes
  31 + ]
  32 +
  33 +
29 34 class CaptchaController(RedditController):
30 35 def GET_captchaimg(self, iden):
31 36 image = captcha.get_image(iden)
5 r2/r2/controllers/embed.py
@@ -34,6 +34,11 @@
34 34
35 35 from r2.controllers.reddit_base import RedditController
36 36
  37 +__all__ = [
  38 + #Constants Only, use @export for functions/classes
  39 + ]
  40 +
  41 +
37 42 @memoize("renderurl_cached", time=60)
38 43 def renderurl_cached(path):
39 44 # Needed so http://reddit.com/help/ works
5 r2/r2/controllers/error.py
@@ -47,6 +47,11 @@
47 47 import os
48 48 os._exit(1)
49 49
  50 +__all__ = [
  51 + #Constants Only, use @export for functions/classes
  52 + ]
  53 +
  54 +
50 55 NUM_FAILIENS = 3
51 56
52 57 redditbroke = \
20 r2/r2/controllers/errorlog.py
@@ -20,16 +20,22 @@
20 20 # Inc. All Rights Reserved.
21 21 ###############################################################################
22 22
23   -from r2.lib.pages import AdminPage, AdminErrorLog
  23 +from r2.lib import pages
24 24
  25 +import r2.controllers.validator as validator
25 26 from r2.controllers.reddit_base import RedditController
26   -from r2.controllers.validator import VAdmin, validate
  27 +from r2.controllers.validator import validate
  28 +
  29 +__all__ = [
  30 + #Constants Only, use @export for functions/classes
  31 + ]
  32 +
27 33
28 34 class ErrorlogController(RedditController):
29   - @validate(VAdmin())
  35 + @validate(validator.VAdmin())
30 36 def GET_index(self):
31   - res = AdminPage(content = AdminErrorLog(),
32   - title = 'error log',
33   - show_sidebar = False
34   - ).render()
  37 + res = pages.AdminPage(content=pages.AdminErrorLog(),
  38 + title='error log',
  39 + show_sidebar=False
  40 + ).render()
35 41 return res
5 r2/r2/controllers/errors.py
@@ -26,6 +26,11 @@
26 26
27 27 from r2.lib.utils import Storage, tup
28 28
  29 +__all__ = [
  30 + #Constants Only, use @export for functions/classes
  31 + ]
  32 +
  33 +
29 34 error_list = dict((
30 35 ('USER_REQUIRED', _("please login to do that")),
31 36 ('HTTPS_REQUIRED', _("this page must be accessed using https")),
19 r2/r2/controllers/feedback.py
@@ -20,18 +20,23 @@
20 20 # Inc. All Rights Reserved.
21 21 ###############################################################################
22 22
23   -from r2.lib.pages import FormPage, SelfServeBlurb, FeedbackBlurb
  23 +from r2.lib import pages
24 24
25 25 from r2.controllers.reddit_base import RedditController
26 26
  27 +__all__ = [
  28 + #Constants Only, use @export for functions/classes
  29 + ]
  30 +
  31 +
27 32 class FeedbackController(RedditController):
28 33
29 34 def GET_ad_inq(self):
30   - return FormPage('advertise',
31   - content = SelfServeBlurb(),
32   - loginbox = False).render()
  35 + return pages.FormPage('advertise',
  36 + content=pages.SelfServeBlurb(),
  37 + loginbox=False).render()
33 38
34 39 def GET_feedback(self):
35   - return FormPage('feedback',
36   - content = FeedbackBlurb(),
37   - loginbox = False).render()
  40 + return pages.FormPage('feedback',
  41 + content=pages.FeedbackBlurb(),
  42 + loginbox=False).render()
819 r2/r2/controllers/front.py
@@ -33,78 +33,25 @@
33 33 from pylons.controllers.util import abort
34 34 from pylons.i18n import _
35 35
  36 +import r2.config as config
36 37 import r2.lib.db.thing as thing
37   -from r2 import config
  38 +import r2.models as models
38 39 from r2.config.extensions import is_api
39   -from r2.lib import sup
  40 +from r2.lib import sup, pages, menus
40 41 from r2.lib.db.thing import NotFound
41 42 from r2.lib.emailer import has_opted_out, Email
42 43 from r2.lib.filters import _force_unicode
43   -from r2.lib.menus import (CommentSortMenu,
44   - NavButton,
45   - NavMenu,
46   - SearchSortMenu,
47   - )
48   -from r2.lib.pages import (AccountActivityPage,
49   - AdminModeInterstitial,
50   - BannedList,
51   - BoringPage,
52   - Captcha,
53   - Cnameframe,
54   - CommentVisitsBox,
55   - CommentPane,
56   - ContributorList,
57   - CreateSubreddit,
58   - DetailsPage,
59   - EditReddit,
60   - EnemyList,
61   - FlairPane,
62   - FormPage,
63   - FrameBuster,
64   - FriendList,
65   - Gold,
66   - GoldPayment,
67   - InfoBar,
68   - LinkCommentSep,
69   - LinkInfoPage,
70   - LoginPage,
71   - ModList,
72   - NewLink,
73   - OptOut,
74   - PaneStack,
75   - Password,
76   - PermalinkMessage,
77   - PrefApps,
78   - PrefDelete,
79   - PrefFeeds,
80   - PrefUpdate,
81   - PrefOptions,
82   - PrefOTP,
83   - PrefsPage,
84   - Reddit,
85   - RedditAds,
86   - RegisterPage,
87   - ResetPassword,
88   - RulesPage,
89   - SearchPage,
90   - SelfServiceOatmeal,
91   - SubredditsPage,
92   - SubredditStylesheet,
93   - Thanks,
94   - TryCompact,
95   - UserAwards,
96   - UserText,
97   - trafficpages,
98   - )
99   -from r2.lib.pages.things import wrap_links, default_thing_wrapper
100   -from r2.lib.search import (SearchQuery,
101   - SubredditSearchQuery,
102   - SearchException,
  44 +from r2.lib.search import (#Classes
103 45 InvalidQuery,
  46 + SearchException,
  47 + SearchQuery,
  48 + SubredditSearchQuery,
104 49 )
105 50 from r2.lib.strings import strings
106 51 from r2.lib.template_helpers import get_domain, add_sr
107   -from r2.lib.utils import (UrlParser,
  52 +from r2.lib.utils import (#Classes
  53 + UrlParser,
  54 + #Functions
108 55 check_cheating,
109 56 link_duplicates,
110 57 link_from_url,
@@ -116,72 +63,32 @@
116 63 )
117 64 from r2.lib.utils.trial_utils import trial_info
118 65 from r2.lib.wrapped import Wrapped
119   -from r2.models import (Account,
120   - AllSR,
121   - Award,
122   - ContribSR,
123   - DefaultSR,
124   - IDBuilder,
125   - EmailVerificationToken,
126   - FakeSubreddit,
127   - Jury,
128   - Link,
129   - LinkListing,
130   - LinkOnTrial,
131   - ModAction,
132   - ModActionListing,
133   - ModContribSR,
134   - ModSR,
135   - MultiReddit,
136   - OAuth2Client,
137   - PasswordResetToken,
138   - QueryBuilder,
139   - SearchBuilder,
140   - Subreddit,
141   - MAX_RECURSION,
142   - Friends,
143   - )
  66 +from r2.lib.wrapper import wrap_links, default_thing_wrapper
144 67
  68 +import r2.controllers.validator as validator
145 69 from r2.controllers.api_docs import api_doc, api_section
146 70 from r2.controllers.errors import errors, UserRequiredException
147 71 from r2.controllers.listingcontroller import ListingController
148   -from r2.controllers.reddit_base import (RedditController,
  72 +from r2.controllers.reddit_base import (#Classes
  73 + RedditController,
  74 + #Functions
149 75 base_listing,
150 76 paginated_listing,
151 77 prevent_framing_and_css
152 78 )
153   -from r2.controllers.validator import (VAdmin,
154   - VBoolean,
155   - VByName,
156   - VCount,
157   - VCommentByID,
158   - VCommentID,
159   - VDestination,
160   - VInt,
161   - VLength,
162   - VLink,
163   - VMenu,
164   - VModhash,
165   - VOneOf,
166   - VOneTimeToken,
167   - VPrintable,
168   - VRequired,
169   - VSponsorAdmin,
170   - VTrafficViewer,
171   - VUser,
172   - can_view_link_comments,
173   - can_comment_link,
174   - nop,
175   - validate,
176   - )
  79 +from r2.controllers.validator import validate
  80 +
  81 +__all__ = [
  82 + #Constants Only, use @export for functions/classes
  83 + ]
177 84
178 85
179 86 class FrontController(RedditController):
180 87
181 88 allow_stylesheets = True
182 89
183   - @validate(article = VLink('article'),
184   - comment = VCommentID('comment'))
  90 + @validate(article=validator.VLink('article'),
  91 + comment=validator.VCommentID('comment'))
185 92 def GET_oldinfo(self, article, type, dest, rest=None, comment=''):
186 93 """Legacy: supporting permalink pages from '06,
187 94 and non-search-engine-friendly links"""
@@ -221,9 +128,9 @@ def GET_random(self):
221 128
222 129 random.shuffle(links)
223 130
224   - builder = IDBuilder(links, skip = True,
225   - keep_fn = lambda x: x.fresh,
226   - num = 1)
  131 + builder = models.IDBuilder(links, skip=True,
  132 + keep_fn=lambda x: x.fresh,
  133 + num=1)
227 134 links = builder.get_items()[0]
228 135
229 136 if links:
@@ -233,19 +140,19 @@ def GET_random(self):
233 140 return self.redirect(add_sr('/'))
234 141
235 142 @prevent_framing_and_css()
236   - @validate(VAdmin(),
237   - thing = VByName('article'),
238   - oldid36 = nop('article'),
239   - after=nop('after'),
240   - before=nop('before'),
241   - count=VCount('count'))
  143 + @validate(validator.VAdmin(),
  144 + thing=validator.VByName('article'),
  145 + oldid36=validator.nop('article'),
  146 + after=validator.nop('after'),
  147 + before=validator.nop('before'),
  148 + count=validator.VCount('count'))
242 149 def GET_details(self, thing, oldid36, after, before, count):
243 150 """The (now deprecated) details page. Content on this page
244 151 has been subsubmed by the presence of the LinkInfoBar on the
245 152 rightbox, so it is only useful for Admin-only wizardry."""
246 153 if not thing:
247 154 try:
248   - link = Link._byID36(oldid36)
  155 + link = models.Link._byID36(oldid36)
249 156 return self.redirect('/details/' + link._fullname)
250 157 except (NotFound, ValueError):
251 158 abort(404)
@@ -257,15 +164,16 @@ def GET_details(self, thing, oldid36, after, before, count):
257 164 else:
258 165 kw['after'] = after
259 166 kw['reverse'] = False
260   - return DetailsPage(thing=thing, expand_children=False, **kw).render()
  167 + return pages.DetailsPage(thing=thing, expand_children=False, **kw
  168 + ).render()
261 169
262 170 def GET_selfserviceoatmeal(self):
263   - return BoringPage(_("self service help"),
264   - show_sidebar = False,
265   - content = SelfServiceOatmeal()).render()
  171 + return pages.BoringPage(_("self service help"),
  172 + show_sidebar=False,
  173 + content=pages.SelfServiceOatmeal()).render()
266 174
267 175
268   - @validate(article = VLink('article'))
  176 + @validate(article=validator.VLink('article'))
269 177 def GET_shirt(self, article):
270 178 if not can_view_link_comments(article):
271 179 abort(403, 'forbidden')
@@ -302,23 +210,23 @@ def _comment_visits(self, article, user, new_visit=None):
302 210 return old_visits
303 211
304 212
305   - @validate(article = VLink('article'),
306   - comment = VCommentID('comment'),
307   - context = VInt('context', min = 0, max = 8),
308   - sort = VMenu('controller', CommentSortMenu),
309   - limit = VInt('limit'),
310   - depth = VInt('depth'))
  213 + @validate(article=validator.VLink('article'),
  214 + comment=validator.VCommentID('comment'),
  215 + context=validator.VInt('context', min = 0, max = 8),
  216 + sort=validator.VMenu('controller', menus.CommentSortMenu),
  217 + limit=validator.VInt('limit'),
  218 + depth=validator.VInt('depth'))
311 219 def POST_comments(self, article, comment, context, sort, limit, depth):
312 220 # VMenu validator will save the value of sort before we reach this
313 221 # point. Now just redirect to GET mode.
314 222 return self.redirect(request.fullpath + query_string(dict(sort=sort)))
315 223
316   - @validate(article = VLink('article'),
317   - comment = VCommentID('comment'),
318   - context = VInt('context', min = 0, max = 8),
319   - sort = VMenu('controller', CommentSortMenu),
320   - limit = VInt('limit'),
321   - depth = VInt('depth'))
  224 + @validate(article=validator.VLink('article'),
  225 + comment=validator.VCommentID('comment'),
  226 + context=validator.VInt('context', min = 0, max = 8),
  227 + sort=validator.VMenu('controller', menus.CommentSortMenu),
  228 + limit=validator.VInt('limit'),
  229 + depth=validator.VInt('depth'))
322 230 @api_doc(api_section.listings,
323 231 uri='/comments/{article}',
324 232 extensions=['json', 'xml'])
@@ -327,7 +235,7 @@ def GET_comments(self, article, comment, context, sort, limit, depth):
327 235 if comment and comment.link_id != article._id:
328 236 return self.abort404()
329 237
330   - sr = Subreddit._byID(article.sr_id, True)
  238 + sr = models.Subreddit._byID(article.sr_id, True)
331 239
332 240 if sr.name == g.takedown_sr:
333 241 request.environ['REDDIT_TAKEDOWN'] = article._fullname
@@ -377,12 +285,12 @@ def GET_comments(self, article, comment, context, sort, limit, depth):
377 285 kw = {}
378 286 # allow depth to be reset (I suspect I'll turn the VInt into a
379 287 # validator on my next pass of .compact)
380   - if depth is not None and 0 < depth < MAX_RECURSION:
  288 + if depth is not None and 0 < depth < models.MAX_RECURSION:
381 289 kw['max_depth'] = depth
382 290 elif c.render_style == "compact":
383 291 kw['max_depth'] = 5
384 292
385   - displayPane = PaneStack()
  293 + displayPane = pages.PaneStack()
386 294
387 295 # allow the user's total count preferences to be overwritten
388 296 # (think of .embed as the use case together with depth=1)
@@ -392,24 +300,25 @@ def GET_comments(self, article, comment, context, sort, limit, depth):
392 300
393 301 if c.user_is_loggedin and c.user.gold:
394 302 if num > g.max_comments_gold:
395   - displayPane.append(InfoBar(message =
396   - strings.over_comment_limit_gold
397   - % max(0, g.max_comments_gold)))
  303 + message = (strings.over_comment_limit_gold %
  304 + max(0, g.max_comments_gold))
  305 + displayPane.append(pages.InfoBar(message=message))
398 306 num = g.max_comments_gold
399 307 elif num > g.max_comments:
400 308 if limit:
401   - displayPane.append(InfoBar(message =
402   - strings.over_comment_limit
403   - % dict(max=max(0, g.max_comments),
404   - goldmax=max(0,
405   - g.max_comments_gold))))
  309 + message = (strings.over_comment_limit %
  310 + dict(max=max(0, g.max_comments),
  311 + goldmax=max(0, g.max_comments_gold))
  312 + )
  313 + displayPane.append(pages.InfoBar(message=message))
406 314 num = g.max_comments
407 315
408 316 # if permalink page, add that message first to the content
409 317 if comment:
410   - displayPane.append(PermalinkMessage(article.make_permalink_slow()))
  318 + permalink = pages.PermalinkMessage(article.make_permalink_slow())
  319 + displayPane.append(permalink)
411 320
412   - displayPane.append(LinkCommentSep())
  321 + displayPane.append(pages.LinkCommentSep())
413 322
414 323 # insert reply box only for logged in user
415 324 if c.user_is_loggedin and can_comment_link(article) and not is_api():
@@ -419,13 +328,14 @@ def GET_comments(self, article, comment, context, sort, limit, depth):
419 328 age = c.start_time - article._date
420 329 if age.days < g.REPLY_AGE_LIMIT:
421 330 display = True
422   - displayPane.append(UserText(item = article, creating = True,
423   - post_form = 'comment',
424   - display = display,
425   - cloneable = True))
  331 + displayPane.append(pages.UserText(item=article,
  332 + creating=True,
  333 + post_form='comment',
  334 + display=display,
  335 + cloneable=True))
426 336
427 337 if previous_visits:
428   - displayPane.append(CommentVisitsBox(previous_visits))
  338 + displayPane.append(pages.CommentVisitsBox(previous_visits))
429 339 # Used in later "more comments" renderings
430 340 pv_hex = md5(repr(previous_visits)).hexdigest()
431 341 g.cache.set(pv_hex, previous_visits, time=g.comment_visits_period)
@@ -435,8 +345,9 @@ def GET_comments(self, article, comment, context, sort, limit, depth):
435 345 c.previous_visits = previous_visits
436 346
437 347 # finally add the comment listing
438   - displayPane.append(CommentPane(article, CommentSortMenu.operator(sort),
439   - comment, context, num, **kw))
  348 + displayPane.append(pages.CommentPane(article,
  349 + menus.CommentSortMenu.operator(sort),
  350 + comment, context, num, **kw))
440 351
441 352 subtitle_buttons = []
442 353
@@ -458,13 +369,14 @@ def GET_comments(self, article, comment, context, sort, limit, depth):
458 369 self._add_show_comments_link(subtitle_buttons, article, num,
459 370 g.max_comments_gold, gold=True)
460 371