Full Code of reddit-archive/reddit for AI

master 753b17407e9a cached
905 files
7.6 MB
2.0M tokens
7491 symbols
1 requests
Download .txt
Showing preview only (8,107K chars total). Download the full file or copy to clipboard to get everything.
Repository: reddit-archive/reddit
Branch: master
Commit: 753b17407e9a
Files: 905
Total size: 7.6 MB

Directory structure:
gitextract_4c_ynblg/

├── .drone.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── SECURITY.md
├── Vagrantfile
├── install/
│   ├── README.md
│   ├── done.sh
│   ├── drone.sh
│   ├── install.cfg
│   ├── install_apt.sh
│   ├── install_cassandra.sh
│   ├── install_services.sh
│   ├── install_zookeeper.sh
│   ├── reddit.sh
│   ├── setup_cassandra.sh
│   ├── setup_mcrouter.sh
│   ├── setup_postgres.sh
│   ├── setup_rabbitmq.sh
│   └── travis.sh
├── install-reddit.sh
├── r2/
│   ├── Makefile
│   ├── Makefile.py
│   ├── babel.cfg
│   ├── check-code
│   ├── coverage.sh
│   ├── pylintrc
│   ├── r2/
│   │   ├── __init__.py
│   │   ├── commands.py
│   │   ├── config/
│   │   │   ├── __init__.py
│   │   │   ├── environment.py
│   │   │   ├── extensions.py
│   │   │   ├── feature/
│   │   │   │   ├── README.md
│   │   │   │   ├── __init__.py
│   │   │   │   ├── feature.py
│   │   │   │   ├── state.py
│   │   │   │   └── world.py
│   │   │   ├── hooks.py
│   │   │   ├── middleware.py
│   │   │   ├── paths.py
│   │   │   ├── queues.py
│   │   │   ├── routing.py
│   │   │   └── templates.py
│   │   ├── controllers/
│   │   │   ├── __init__.py
│   │   │   ├── admin.py
│   │   │   ├── api.py
│   │   │   ├── api_docs.py
│   │   │   ├── apiv1/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── gold.py
│   │   │   │   ├── login.py
│   │   │   │   ├── scopes.py
│   │   │   │   └── user.py
│   │   │   ├── awards.py
│   │   │   ├── buttons.py
│   │   │   ├── captcha.py
│   │   │   ├── embed.py
│   │   │   ├── error.py
│   │   │   ├── front.py
│   │   │   ├── googletagmanager.py
│   │   │   ├── health.py
│   │   │   ├── ipn.py
│   │   │   ├── listingcontroller.py
│   │   │   ├── login.py
│   │   │   ├── mailgun.py
│   │   │   ├── mediaembed.py
│   │   │   ├── multi.py
│   │   │   ├── newsletter.py
│   │   │   ├── oauth2.py
│   │   │   ├── oembed.py
│   │   │   ├── policies.py
│   │   │   ├── post.py
│   │   │   ├── promotecontroller.py
│   │   │   ├── reddit_base.py
│   │   │   ├── redirect.py
│   │   │   ├── robots.py
│   │   │   ├── toolbar.py
│   │   │   ├── web.py
│   │   │   └── wiki.py
│   │   ├── data/
│   │   │   └── locations.json
│   │   ├── lib/
│   │   │   ├── __init__.py
│   │   │   ├── amqp.py
│   │   │   ├── app_globals.py
│   │   │   ├── authorize/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── api.py
│   │   │   │   └── interaction.py
│   │   │   ├── automoderator.py
│   │   │   ├── base.py
│   │   │   ├── baseplate_integration.py
│   │   │   ├── butler.py
│   │   │   ├── c/
│   │   │   │   └── filters.c
│   │   │   ├── cache.py
│   │   │   ├── cache_poisoning.py
│   │   │   ├── captcha.py
│   │   │   ├── comment_tree.py
│   │   │   ├── configparse.py
│   │   │   ├── contrib/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── activity.thrift
│   │   │   │   ├── dtds/
│   │   │   │   │   ├── README
│   │   │   │   │   └── allowed_entities.dtd
│   │   │   │   ├── ipaddress.py
│   │   │   │   ├── rcssmin.py
│   │   │   │   └── simpleflake.py
│   │   │   ├── cookies.py
│   │   │   ├── count.py
│   │   │   ├── csrf.py
│   │   │   ├── cssfilter.py
│   │   │   ├── db/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── _sorts.pyx
│   │   │   │   ├── alter_db.py
│   │   │   │   ├── operators.py
│   │   │   │   ├── queries.py
│   │   │   │   ├── sorts.py
│   │   │   │   ├── tdb_cassandra.py
│   │   │   │   ├── tdb_lite.py
│   │   │   │   ├── tdb_sql.py
│   │   │   │   ├── thing.py
│   │   │   │   └── userrel.py
│   │   │   ├── einhorn.py
│   │   │   ├── emailer.py
│   │   │   ├── embeds.py
│   │   │   ├── emr_helpers.py
│   │   │   ├── errors.py
│   │   │   ├── eventcollector.py
│   │   │   ├── export.py
│   │   │   ├── filters.py
│   │   │   ├── generate_strings.py
│   │   │   ├── geoip.py
│   │   │   ├── gzipper.py
│   │   │   ├── hadoop_decompress.py
│   │   │   ├── hardcachebackend.py
│   │   │   ├── helpers.py
│   │   │   ├── hooks.py
│   │   │   ├── inventory.py
│   │   │   ├── inventory_optimization.py
│   │   │   ├── ip_events.py
│   │   │   ├── js.py
│   │   │   ├── jsonresponse.py
│   │   │   ├── jsontemplates.py
│   │   │   ├── language.py
│   │   │   ├── lock.py
│   │   │   ├── log.py
│   │   │   ├── loid.py
│   │   │   ├── manager/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── db_manager.py
│   │   │   │   └── tp_manager.py
│   │   │   ├── media.py
│   │   │   ├── memoize.py
│   │   │   ├── menus.py
│   │   │   ├── merge.py
│   │   │   ├── message_to_email.py
│   │   │   ├── migrate/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── campaigns_to_things.py
│   │   │   │   ├── migrate.py
│   │   │   │   ├── mr_domains.py
│   │   │   │   ├── mr_permacache.py
│   │   │   │   └── vote_details_ip_backfill.py
│   │   │   ├── mr_tools/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── _mr_tools.pyx
│   │   │   │   └── mr_tools.py
│   │   │   ├── mr_top.py
│   │   │   ├── newsletter.py
│   │   │   ├── normalized_hot.py
│   │   │   ├── nymph.py
│   │   │   ├── organic.py
│   │   │   ├── pages/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── admin_pages.py
│   │   │   │   ├── pages.py
│   │   │   │   ├── things.py
│   │   │   │   ├── trafficpages.py
│   │   │   │   └── wiki.py
│   │   │   ├── permissions.py
│   │   │   ├── plugin.py
│   │   │   ├── profiler.py
│   │   │   ├── promote.py
│   │   │   ├── providers/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── auth/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── cookie.py
│   │   │   │   │   └── http.py
│   │   │   │   ├── cdn/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── cloudflare.py
│   │   │   │   │   ├── fastly.py
│   │   │   │   │   └── null.py
│   │   │   │   ├── email/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── mailgun.py
│   │   │   │   │   └── null.py
│   │   │   │   ├── image_resizing/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── imgix.py
│   │   │   │   │   ├── no_op.py
│   │   │   │   │   └── unsplashit.py
│   │   │   │   ├── media/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── filesystem.py
│   │   │   │   │   └── s3.py
│   │   │   │   ├── search/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── cloudsearch.py
│   │   │   │   │   ├── common.py
│   │   │   │   │   └── solr.py
│   │   │   │   └── support/
│   │   │   │       ├── __init__.py
│   │   │   │       └── zendesk.py
│   │   │   ├── ratelimit.py
│   │   │   ├── recommender.py
│   │   │   ├── require.py
│   │   │   ├── rising.py
│   │   │   ├── s3_helpers.py
│   │   │   ├── sgm.pyx
│   │   │   ├── signing.py
│   │   │   ├── sitemaps/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── data.py
│   │   │   │   ├── generate.py
│   │   │   │   ├── store.py
│   │   │   │   └── watcher.py
│   │   │   ├── souptest.py
│   │   │   ├── sr_pops.py
│   │   │   ├── static.py
│   │   │   ├── stats.py
│   │   │   ├── strings.py
│   │   │   ├── subreddit_search.py
│   │   │   ├── support_tickets.py
│   │   │   ├── system_messages.py
│   │   │   ├── takedowns.py
│   │   │   ├── template_helpers.py
│   │   │   ├── totp.py
│   │   │   ├── tracking.py
│   │   │   ├── traffic/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── emr_traffic.py
│   │   │   │   └── traffic.py
│   │   │   ├── translation.py
│   │   │   ├── trending.py
│   │   │   ├── unicode.py
│   │   │   ├── utils/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── _utils.pyx
│   │   │   │   ├── comment_tree_utils.pyx
│   │   │   │   ├── feature_utils.py
│   │   │   │   ├── http_utils.py
│   │   │   │   ├── reddit_agent_parser.py
│   │   │   │   └── utils.py
│   │   │   ├── validator/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── preferences.py
│   │   │   │   ├── validator.py
│   │   │   │   └── wiki.py
│   │   │   ├── voting.py
│   │   │   ├── websockets.py
│   │   │   ├── wrapped.pyx
│   │   │   └── zookeeper.py
│   │   ├── models/
│   │   │   ├── __init__.py
│   │   │   ├── account.py
│   │   │   ├── admin_notes.py
│   │   │   ├── admintools.py
│   │   │   ├── automoderator.py
│   │   │   ├── award.py
│   │   │   ├── bans.py
│   │   │   ├── bidding.py
│   │   │   ├── builder.py
│   │   │   ├── comment_tree.py
│   │   │   ├── flair.py
│   │   │   ├── gold.py
│   │   │   ├── ip.py
│   │   │   ├── keyvalue.py
│   │   │   ├── last_modified.py
│   │   │   ├── link.py
│   │   │   ├── listing.py
│   │   │   ├── mail_queue.py
│   │   │   ├── media_cache.py
│   │   │   ├── modaction.py
│   │   │   ├── printable.py
│   │   │   ├── promo.py
│   │   │   ├── promo_metrics.py
│   │   │   ├── query_cache.py
│   │   │   ├── recommend.py
│   │   │   ├── report.py
│   │   │   ├── rules.py
│   │   │   ├── subreddit.py
│   │   │   ├── token.py
│   │   │   ├── traffic.py
│   │   │   ├── trylater.py
│   │   │   ├── vote.py
│   │   │   └── wiki.py
│   │   ├── public/
│   │   │   └── static/
│   │   │       ├── button/
│   │   │       │   ├── button-embed.js
│   │   │       │   ├── button1.html
│   │   │       │   ├── button1.js
│   │   │       │   ├── button2.html
│   │   │       │   ├── button2.js
│   │   │       │   ├── button3.html
│   │   │       │   └── button3.js
│   │   │       ├── css/
│   │   │       │   ├── adminbar.less
│   │   │       │   ├── compact.css
│   │   │       │   ├── compact.scss
│   │   │       │   ├── components/
│   │   │       │   │   ├── alerts.less
│   │   │       │   │   ├── animations.less
│   │   │       │   │   ├── buttons.less
│   │   │       │   │   ├── close.less
│   │   │       │   │   ├── colors.less
│   │   │       │   │   ├── components.less
│   │   │       │   │   ├── form-states.less
│   │   │       │   │   ├── forms.less
│   │   │       │   │   ├── image-upload.less
│   │   │       │   │   ├── infobar.less
│   │   │       │   │   ├── mixins.less
│   │   │       │   │   ├── modal.less
│   │   │       │   │   ├── progress.less
│   │   │       │   │   ├── read-next.less
│   │   │       │   │   ├── strength-meter.less
│   │   │       │   │   ├── toggles.less
│   │   │       │   │   ├── tooltip.less
│   │   │       │   │   ├── utils.less
│   │   │       │   │   └── variables.less
│   │   │       │   ├── config.rb
│   │   │       │   ├── expando.less
│   │   │       │   ├── highlight.css
│   │   │       │   ├── interstitial.less
│   │   │       │   ├── markdown.less
│   │   │       │   ├── mobile.css
│   │   │       │   ├── mod-action-icons.less
│   │   │       │   ├── modtools.less
│   │   │       │   ├── policies.less
│   │   │       │   ├── post-sharing.less
│   │   │       │   ├── reddit-embed.less
│   │   │       │   ├── reddit.less
│   │   │       │   ├── search.less
│   │   │       │   ├── subreddit-rules.less
│   │   │       │   └── wiki.less
│   │   │       ├── inbound-email-policy.html
│   │   │       ├── js/
│   │   │       │   ├── access.js
│   │   │       │   ├── action-forms.js
│   │   │       │   ├── actions.js
│   │   │       │   ├── adminbar.js
│   │   │       │   ├── ajax.js
│   │   │       │   ├── analytics.js
│   │   │       │   ├── apps.js
│   │   │       │   ├── archived.js
│   │   │       │   ├── backbone-init.js
│   │   │       │   ├── base.js
│   │   │       │   ├── bootstrap.tooltip.extension.js
│   │   │       │   ├── cache-poisoning-detection.js
│   │   │       │   ├── client-error-logger.js
│   │   │       │   ├── compact.js
│   │   │       │   ├── custom-event.js
│   │   │       │   ├── do-not-track.js
│   │   │       │   ├── edit-subreddit-rules.js
│   │   │       │   ├── embed/
│   │   │       │   │   ├── comment-embed.js
│   │   │       │   │   ├── embed.js
│   │   │       │   │   ├── pixel-tracking.js
│   │   │       │   │   └── utils.js
│   │   │       │   ├── embed.js
│   │   │       │   ├── errors.js
│   │   │       │   ├── events.js
│   │   │       │   ├── expando/
│   │   │       │   │   ├── nsfwflow.js
│   │   │       │   │   └── nsfwgate.html
│   │   │       │   ├── expando.js
│   │   │       │   ├── filter.js
│   │   │       │   ├── flair.js
│   │   │       │   ├── frames.js
│   │   │       │   ├── gate-popup.js
│   │   │       │   ├── gold.js
│   │   │       │   ├── google-tag-manager/
│   │   │       │   │   ├── gtm-jail-listener.js
│   │   │       │   │   ├── gtm-listener.js
│   │   │       │   │   └── gtm.js
│   │   │       │   ├── highlight.js
│   │   │       │   ├── hooks.js
│   │   │       │   ├── https-tester.js
│   │   │       │   ├── i18n.js
│   │   │       │   ├── image-upload.js
│   │   │       │   ├── interestbar.js
│   │   │       │   ├── jail.js
│   │   │       │   ├── jquery.reddit.js
│   │   │       │   ├── lib/
│   │   │       │   │   ├── backbone-1.0.0.js
│   │   │       │   │   ├── bootstrap.modal.js
│   │   │       │   │   ├── bootstrap.tooltip.js
│   │   │       │   │   ├── bootstrap.transition.js
│   │   │       │   │   ├── es5-sham.js
│   │   │       │   │   ├── es5-shim.js
│   │   │       │   │   ├── event-tracker.js
│   │   │       │   │   ├── highlight.pack.js
│   │   │       │   │   ├── hmac-sha256.js
│   │   │       │   │   ├── html5shiv.js
│   │   │       │   │   ├── jed.js
│   │   │       │   │   ├── jquery-1.11.1.js
│   │   │       │   │   ├── jquery-2.1.1.js
│   │   │       │   │   ├── jquery.cookie.js
│   │   │       │   │   ├── jquery.flot.js
│   │   │       │   │   ├── jquery.flot.time.js
│   │   │       │   │   ├── jquery.lazyload.js
│   │   │       │   │   ├── jquery.url.js
│   │   │       │   │   ├── json2.js
│   │   │       │   │   ├── less-1.4.2.js
│   │   │       │   │   ├── modernizr.js
│   │   │       │   │   ├── react-with-addons-0.11.2.js
│   │   │       │   │   ├── reddit-client-lib.js
│   │   │       │   │   ├── store.js
│   │   │       │   │   ├── ui.core.js
│   │   │       │   │   ├── ui.datepicker.js
│   │   │       │   │   └── underscore-1.4.4-1.js
│   │   │       │   ├── link-click-tracking.js
│   │   │       │   ├── locked.js
│   │   │       │   ├── logging.js
│   │   │       │   ├── login.js
│   │   │       │   ├── messagecompose.js
│   │   │       │   ├── messages.js
│   │   │       │   ├── migrate-global-reddit.js
│   │   │       │   ├── models/
│   │   │       │   │   ├── subreddit-rule.js
│   │   │       │   │   └── validators.js
│   │   │       │   ├── multi.js
│   │   │       │   ├── newsletter.js
│   │   │       │   ├── policies.js
│   │   │       │   ├── popup.js
│   │   │       │   ├── post-sharing.js
│   │   │       │   ├── preload.js
│   │   │       │   ├── qrcode.js
│   │   │       │   ├── recommender.js
│   │   │       │   ├── reddit-hook.js
│   │   │       │   ├── reddit-init-hook.js
│   │   │       │   ├── reddit.js
│   │   │       │   ├── report.js
│   │   │       │   ├── safe-store.js
│   │   │       │   ├── saved.js
│   │   │       │   ├── scrollupdater.js
│   │   │       │   ├── setup.js
│   │   │       │   ├── sponsored.js
│   │   │       │   ├── spotlight.js
│   │   │       │   ├── sr-autocomplete.js
│   │   │       │   ├── stateify.js
│   │   │       │   ├── strength-meter.js
│   │   │       │   ├── synced-session-storage.js
│   │   │       │   ├── templates.js
│   │   │       │   ├── timeouts.js
│   │   │       │   ├── timeseries.js
│   │   │       │   ├── timetext.js
│   │   │       │   ├── timings.js
│   │   │       │   ├── toggles.js
│   │   │       │   ├── traffic.js
│   │   │       │   ├── ui/
│   │   │       │   │   ├── formbar.html
│   │   │       │   │   └── formbar.js
│   │   │       │   ├── ui.js
│   │   │       │   ├── uibase.js
│   │   │       │   ├── utils.js
│   │   │       │   ├── uuid.js
│   │   │       │   ├── validator.js
│   │   │       │   ├── visited.js
│   │   │       │   ├── voting.js
│   │   │       │   ├── warn-on-unload.js
│   │   │       │   ├── websocket.js
│   │   │       │   └── wiki.js
│   │   │       ├── opensearch.xml
│   │   │       └── sureroute.html
│   │   ├── templates/
│   │   │   ├── __init__.py
│   │   │   ├── accountactivitybox.html
│   │   │   ├── adminawardgive.html
│   │   │   ├── adminawards.html
│   │   │   ├── adminawardwinners.html
│   │   │   ├── adminbar.html
│   │   │   ├── admincreddits.html
│   │   │   ├── adminerrorlog.html
│   │   │   ├── admingold.html
│   │   │   ├── admininterstitial.html
│   │   │   ├── adminnotessidebar.html
│   │   │   ├── ads.html
│   │   │   ├── adverttrafficsummary.html
│   │   │   ├── allinfobar.html
│   │   │   ├── apihelp.html
│   │   │   ├── archivedinterstitial.html
│   │   │   ├── automoderatorconfig.html
│   │   │   ├── awardreceived.html
│   │   │   ├── bannedinterstitial.html
│   │   │   ├── banneduserinterstitial.html
│   │   │   ├── base.compact
│   │   │   ├── base.html
│   │   │   ├── base.htmllite
│   │   │   ├── base.iframe
│   │   │   ├── base.mobile
│   │   │   ├── base.xml
│   │   │   ├── buttondemopanel.html
│   │   │   ├── buttonlite.js
│   │   │   ├── captcha.compact
│   │   │   ├── captcha.html
│   │   │   ├── clickgadget.html
│   │   │   ├── clientinfobar.compact
│   │   │   ├── clientinfobar.html
│   │   │   ├── comment.compact
│   │   │   ├── comment.html
│   │   │   ├── comment.htmllite
│   │   │   ├── comment.iframe
│   │   │   ├── comment.mobile
│   │   │   ├── comment.xml
│   │   │   ├── comment_skeleton.html
│   │   │   ├── commentvisitsbox.html
│   │   │   ├── commentvisitsbox.xml
│   │   │   ├── confirmawardclaim.html
│   │   │   ├── contactus.html
│   │   │   ├── createsubreddit.html
│   │   │   ├── creditgild.html
│   │   │   ├── crossdomain.xml
│   │   │   ├── csserror.html
│   │   │   ├── debugfooter.html
│   │   │   ├── deleteduserinterstitial.html
│   │   │   ├── emailchangeemail.email
│   │   │   ├── embed.html
│   │   │   ├── errorpage.compact
│   │   │   ├── errorpage.html
│   │   │   ├── exploreitem.html
│   │   │   ├── exploreitemlisting.html
│   │   │   ├── filteredinfobar.html
│   │   │   ├── flairlist.html
│   │   │   ├── flairlistrow.html
│   │   │   ├── flairnextlink.html
│   │   │   ├── flairpane.html
│   │   │   ├── flairprefs.html
│   │   │   ├── flairselector.html
│   │   │   ├── flairselectorlinksample.html
│   │   │   ├── flairtemplateeditor.html
│   │   │   ├── flairtemplatelist.html
│   │   │   ├── flairtemplatesample.html
│   │   │   ├── fraudform.html
│   │   │   ├── geotargetnotice.html
│   │   │   ├── gettextheader.html
│   │   │   ├── gilding.html
│   │   │   ├── gold.html
│   │   │   ├── goldgiftcodeemail.email
│   │   │   ├── goldonlyinterstitial.html
│   │   │   ├── goldpayment.compact
│   │   │   ├── goldpayment.html
│   │   │   ├── goldpayment.htmllite
│   │   │   ├── goldpayment.mobile
│   │   │   ├── goldpayment.xml
│   │   │   ├── goldsubscription.html
│   │   │   ├── goldthanks.html
│   │   │   ├── goldvertisement.html
│   │   │   ├── googletagmanager.html
│   │   │   ├── googletagmanagerjail.html
│   │   │   ├── headerbar.mobile
│   │   │   ├── helplink.html
│   │   │   ├── helppage.html
│   │   │   ├── infobar.html
│   │   │   ├── interestbar.html
│   │   │   ├── interstitial.html
│   │   │   ├── intimeoutinterstitial.html
│   │   │   ├── languagetrafficsummary.html
│   │   │   ├── less.html
│   │   │   ├── link.compact
│   │   │   ├── link.html
│   │   │   ├── link.htmllite
│   │   │   ├── link.mobile
│   │   │   ├── link.xml
│   │   │   ├── linkcommentsep.mobile
│   │   │   ├── linkcommentssettings.compact
│   │   │   ├── linkcommentssettings.html
│   │   │   ├── linkinfobar.html
│   │   │   ├── linkinfopage.htmllite
│   │   │   ├── linkinfopage.iframe
│   │   │   ├── linklisting.html
│   │   │   ├── listing.compact
│   │   │   ├── listing.html
│   │   │   ├── listing.htmllite
│   │   │   ├── listing.iframe
│   │   │   ├── listing.mobile
│   │   │   ├── listing.xml
│   │   │   ├── listingchooser.html
│   │   │   ├── listingsuggestions.html
│   │   │   ├── locationbar.html
│   │   │   ├── lockedinterstitial.html
│   │   │   ├── login.compact
│   │   │   ├── login.html
│   │   │   ├── loginformwide.html
│   │   │   ├── mail_opt.email
│   │   │   ├── mediaembed.html
│   │   │   ├── mediaembedbody.html
│   │   │   ├── mediapreview.html
│   │   │   ├── menuarea.compact
│   │   │   ├── menuarea.html
│   │   │   ├── menulink.compact
│   │   │   ├── menulink.html
│   │   │   ├── message.compact
│   │   │   ├── message.html
│   │   │   ├── message.xml
│   │   │   ├── messagecompose.compact
│   │   │   ├── messagecompose.html
│   │   │   ├── messagenotificationemail.email
│   │   │   ├── messagenotificationemailsunsubscribe.html
│   │   │   ├── mobilewebredirectbar.compact
│   │   │   ├── modaction.html
│   │   │   ├── modaction.xml
│   │   │   ├── moderatormessagecompose.html
│   │   │   ├── moderatorpermissions.html
│   │   │   ├── modlisting.html
│   │   │   ├── modsrinfobar.html
│   │   │   ├── modtoolspage.html
│   │   │   ├── morechildren.compact
│   │   │   ├── morechildren.html
│   │   │   ├── moremessages.html
│   │   │   ├── morerecursion.compact
│   │   │   ├── morerecursion.html
│   │   │   ├── morerecursion.iframe
│   │   │   ├── multiinfobar.html
│   │   │   ├── navbutton.compact
│   │   │   ├── navbutton.html
│   │   │   ├── navbutton.mobile
│   │   │   ├── navmenu.compact
│   │   │   ├── navmenu.html
│   │   │   ├── navmenu.mobile
│   │   │   ├── newlink.compact
│   │   │   ├── newlink.html
│   │   │   ├── newsletter.html
│   │   │   ├── newsletterbar.html
│   │   │   ├── oauth2authorization.compact
│   │   │   ├── oauth2authorization.html
│   │   │   ├── optout.html
│   │   │   ├── over18interstitial.html
│   │   │   ├── pagenamenav.compact
│   │   │   ├── pagenamenav.html
│   │   │   ├── pagenamenav.mobile
│   │   │   ├── panestack.compact
│   │   │   ├── panestack.html
│   │   │   ├── panestack.htmllite
│   │   │   ├── panestack.iframe
│   │   │   ├── panestack.mobile
│   │   │   ├── panestack.xml
│   │   │   ├── password.html
│   │   │   ├── passwordchangeemail.email
│   │   │   ├── passwordreset.email
│   │   │   ├── paymentform.html
│   │   │   ├── permalinkmessage.html
│   │   │   ├── policypage.html
│   │   │   ├── policyview.html
│   │   │   ├── popup.html
│   │   │   ├── prefapps.html
│   │   │   ├── prefdeactivate.html
│   │   │   ├── preffeeds.html
│   │   │   ├── prefoptions.html
│   │   │   ├── prefsecurity.html
│   │   │   ├── prefupdate.html
│   │   │   ├── printable.compact
│   │   │   ├── printable.html
│   │   │   ├── printable.htmllite
│   │   │   ├── printable.iframe
│   │   │   ├── printable.mobile
│   │   │   ├── printablebuttons.html
│   │   │   ├── privateinterstitial.html
│   │   │   ├── profilebar.html
│   │   │   ├── promo_email.email
│   │   │   ├── promotedlink.html
│   │   │   ├── promotedlinktraffic.html
│   │   │   ├── promoteinventory.html
│   │   │   ├── promotelinkbase.html
│   │   │   ├── promotelinkedit.html
│   │   │   ├── promotelinknew.html
│   │   │   ├── promotereport.html
│   │   │   ├── quarantineinterstitial.html
│   │   │   ├── ratelimit_base.html
│   │   │   ├── ratelimit_throttled.html
│   │   │   ├── ratelimit_toofast.html
│   │   │   ├── rawcode.html
│   │   │   ├── readnext.html
│   │   │   ├── readnextlink.html
│   │   │   ├── readnextlisting.html
│   │   │   ├── reddit.compact
│   │   │   ├── reddit.html
│   │   │   ├── reddit.htmllite
│   │   │   ├── reddit.mobile
│   │   │   ├── reddit.xml
│   │   │   ├── redditfooter.html
│   │   │   ├── redditheader.compact
│   │   │   ├── redditheader.html
│   │   │   ├── redditheader.mobile
│   │   │   ├── redditinfobar.html
│   │   │   ├── reddittraffic.html
│   │   │   ├── refundpage.html
│   │   │   ├── register.compact
│   │   │   ├── registrationinfo.html
│   │   │   ├── renderablecampaign.html
│   │   │   ├── reportform.html
│   │   │   ├── reportformtemplates.html
│   │   │   ├── resetpassword.html
│   │   │   ├── robots.txt
│   │   │   ├── rules.html
│   │   │   ├── searchbar.compact
│   │   │   ├── searchbar.html
│   │   │   ├── searchform.compact
│   │   │   ├── searchform.html
│   │   │   ├── searchlisting.html
│   │   │   ├── searchresultbase.html
│   │   │   ├── searchresultlink.html
│   │   │   ├── searchresultsubreddit.html
│   │   │   ├── selftext.html
│   │   │   ├── serversecondsbar.html
│   │   │   ├── share.email
│   │   │   ├── shareclose.html
│   │   │   ├── sidebarmessage.html
│   │   │   ├── sidebarmodlist.html
│   │   │   ├── sidebarmultilist.html
│   │   │   ├── sidebox.html
│   │   │   ├── sidecontentbox.html
│   │   │   ├── sitewidetraffic.html
│   │   │   ├── sitewidetrafficpage.html
│   │   │   ├── sponsorlookupuser.html
│   │   │   ├── sponsorshipbox.html
│   │   │   ├── sponsorsidebar.html
│   │   │   ├── spotlightlisting.html
│   │   │   ├── starkcomment.html
│   │   │   ├── subreddit.compact
│   │   │   ├── subreddit.html
│   │   │   ├── subreddit.mobile
│   │   │   ├── subreddit.xml
│   │   │   ├── subredditfacets.html
│   │   │   ├── subredditinfobar.html
│   │   │   ├── subredditreportform.html
│   │   │   ├── subredditselector.html
│   │   │   ├── subredditstylesheet.html
│   │   │   ├── subredditstylesheetbase.html
│   │   │   ├── subredditstylesheetsource.html
│   │   │   ├── subreddittopbar.html
│   │   │   ├── subreddittraffic.html
│   │   │   ├── subreddittrafficreport.html
│   │   │   ├── subscribebutton.html
│   │   │   ├── subscriptionbox.html
│   │   │   ├── suspiciouspaymentemail.email
│   │   │   ├── tabbedpane.html
│   │   │   ├── tablelisting.html
│   │   │   ├── takedownpane.compact
│   │   │   ├── takedownpane.html
│   │   │   ├── thanks.html
│   │   │   ├── thingupdater.html
│   │   │   ├── timeserieschart.html
│   │   │   ├── trafficpage.html
│   │   │   ├── trendingsubredditsbar.html
│   │   │   ├── trophycase.html
│   │   │   ├── trycompact.compact
│   │   │   ├── unreadmessagessuggestions.html
│   │   │   ├── uploadedadsimage.html
│   │   │   ├── uploadedimage.html
│   │   │   ├── userawards.html
│   │   │   ├── userblockedinterstitial.html
│   │   │   ├── useriphistory.html
│   │   │   ├── userlisting.html
│   │   │   ├── usertableitem.html
│   │   │   ├── usertext.compact
│   │   │   ├── usertext.html
│   │   │   ├── usertext.mobile
│   │   │   ├── utils/
│   │   │   │   └── gold.html
│   │   │   ├── utils.compact
│   │   │   ├── utils.html
│   │   │   ├── utils.xml
│   │   │   ├── verifyemail.email
│   │   │   ├── welcomebar.html
│   │   │   ├── widgetdemopanel.html
│   │   │   ├── wikibasepage.html
│   │   │   ├── wikieditpage.html
│   │   │   ├── wikipagediscussions.html
│   │   │   ├── wikipagediscussions.xml
│   │   │   ├── wikipagelisting.html
│   │   │   ├── wikipagenotfound.html
│   │   │   ├── wikipagerevisions.html
│   │   │   ├── wikipagerevisions.xml
│   │   │   ├── wikipagesettings.html
│   │   │   ├── wikirevision.html
│   │   │   ├── wikirevision.xml
│   │   │   ├── wikiview.compact
│   │   │   ├── wikiview.html
│   │   │   ├── wrappeduser.compact
│   │   │   ├── wrappeduser.html
│   │   │   └── wrappeduser.mobile
│   │   └── tests/
│   │       ├── __init__.py
│   │       ├── functional/
│   │       │   ├── __init__.py
│   │       │   └── controller/
│   │       │       ├── __init__.py
│   │       │       ├── del_msg_test.py
│   │       │       ├── login/
│   │       │       │   ├── __init__.py
│   │       │       │   ├── api_tests.py
│   │       │       │   ├── apiv1_tests.py
│   │       │       │   ├── common.py
│   │       │       │   └── post_tests.py
│   │       │       └── prefs/
│   │       │           └── __init__.py
│   │       └── unit/
│   │           ├── __init__.py
│   │           ├── config/
│   │           │   ├── __init__.py
│   │           │   ├── experiment_test.py
│   │           │   └── feature_test.py
│   │           ├── lib/
│   │           │   ├── __init__.py
│   │           │   ├── authorize/
│   │           │   │   ├── __init__.py
│   │           │   │   ├── test_api.py
│   │           │   │   └── test_interaction.py
│   │           │   ├── configparse_test.py
│   │           │   ├── cookie_upgrade_test.py
│   │           │   ├── cssfilter_test.py
│   │           │   ├── eventcollector_tests.py
│   │           │   ├── js_test.py
│   │           │   ├── loid_tests.py
│   │           │   ├── media_test.py
│   │           │   ├── permissions_test.py
│   │           │   ├── promote_test.py
│   │           │   ├── providers/
│   │           │   │   ├── __init__.py
│   │           │   │   └── image_resizing/
│   │           │   │       ├── __init__.py
│   │           │   │       ├── imgix_test.py
│   │           │   │       ├── no_op_test.py
│   │           │   │       └── unsplashit_test.py
│   │           │   ├── reddit_agent_parser_test.py
│   │           │   ├── signing_tests.py
│   │           │   ├── souptest_test.py
│   │           │   ├── stats_test.py
│   │           │   ├── tracking_test.py
│   │           │   ├── urlparser_test.py
│   │           │   ├── utils_test.py
│   │           │   └── validator/
│   │           │       ├── __init__.py
│   │           │       ├── test_validator.py
│   │           │       └── test_vverifypassword.py
│   │           ├── models/
│   │           │   ├── __init__.py
│   │           │   ├── commentbuilder_test.py
│   │           │   ├── link_test.py
│   │           │   ├── promo_test.py
│   │           │   ├── subreddit_test.py
│   │           │   ├── thing_test.py
│   │           │   ├── user_message_builder_test.py
│   │           │   └── vote_test.py
│   │           └── ratelimit_test.py
│   ├── setup.cfg
│   ├── setup.py
│   └── updateini.py
├── scripts/
│   ├── add_to_collection
│   ├── compute_time_listings
│   ├── geoip_service.py
│   ├── hashdist.py
│   ├── inject_test_data.py
│   ├── manage-consumers
│   ├── migrate/
│   │   ├── backfill/
│   │   │   ├── comment_scores_by_link.py
│   │   │   ├── fix_preview_images.py
│   │   │   ├── gilded_by_subreddit.py
│   │   │   ├── gilded_comments.py
│   │   │   ├── gilded_user_comments.py
│   │   │   ├── modaction_by_srandmod.py
│   │   │   ├── modmsgtime.py
│   │   │   ├── msgtime_to_inbox_count.py
│   │   │   ├── num_gildings.py
│   │   │   ├── scrub_deleted_users.py
│   │   │   ├── srmember_to_cassandra.py
│   │   │   ├── subreddit_images.py
│   │   │   └── user_gildings.py
│   │   ├── comment-participation.pig
│   │   ├── dump-all.sh
│   │   ├── dump-rel.sh
│   │   ├── dump-thing.sh
│   │   ├── example.sh
│   │   ├── regenerate-query-cache.py
│   │   ├── run-query.sh
│   │   ├── tuples_to_sstables.py
│   │   └── udfs/
│   │       ├── build.xml
│   │       └── src/
│   │           └── com/
│   │               └── reddit/
│   │                   └── pig/
│   │                       ├── MAKE_FULLNAME.java
│   │                       ├── MAKE_MAP.java
│   │                       ├── MAKE_ROWKEY.java
│   │                       ├── MAKE_THING2_FULLNAME.java
│   │                       ├── TO_36.java
│   │                       ├── TO_JSON.java
│   │                       └── TypeID.java
│   ├── promoted_links.py
│   ├── read_secrets
│   ├── saferun.sh
│   ├── stylecheck_git_diff.sh
│   ├── tracker.py
│   ├── traffic/
│   │   ├── Makefile
│   │   ├── decrypt_userinfo.c
│   │   ├── mr_aggregate.pig
│   │   ├── mr_coalesce.pig
│   │   ├── mr_process_hour.pig
│   │   ├── parse.c
│   │   ├── traffic_bootstrap.sh
│   │   ├── utils.c
│   │   ├── utils.h
│   │   └── verify.c
│   ├── upload_static_files_to_s3.py
│   ├── wrap-job
│   ├── write_live_config
│   └── write_secrets
├── solr/
│   ├── README.md
│   ├── schema.xml
│   └── schema4.xml
└── upstart/
    ├── reddit-boot.conf
    ├── reddit-consumer-author_query_q.conf
    ├── reddit-consumer-automoderator_q.conf
    ├── reddit-consumer-butler_q.conf
    ├── reddit-consumer-commentstree_q.conf
    ├── reddit-consumer-del_account_q.conf
    ├── reddit-consumer-domain_query_q.conf
    ├── reddit-consumer-event_collector_q.conf
    ├── reddit-consumer-markread_q.conf
    ├── reddit-consumer-modmail_email_q.conf
    ├── reddit-consumer-newcomments_q.conf
    ├── reddit-consumer-scraper_q.conf
    ├── reddit-consumer-search_q.conf
    ├── reddit-consumer-sitemaps_q.conf
    ├── reddit-consumer-subreddit_query_q.conf
    ├── reddit-consumer-vote_comment_q.conf
    ├── reddit-consumer-vote_link_q.conf
    ├── reddit-consumers-restart.conf
    ├── reddit-consumers-start.conf
    ├── reddit-job-broken_things.conf
    ├── reddit-job-clean_up_hardcache.conf
    ├── reddit-job-email.conf
    ├── reddit-job-hourly_traffic.conf
    ├── reddit-job-rising.conf
    ├── reddit-job-subscribers.conf
    ├── reddit-job-trylater.conf
    ├── reddit-job-update_geoip.conf
    ├── reddit-job-update_gold_users.conf
    ├── reddit-job-update_popular_subreddits.conf
    ├── reddit-job-update_promo_metrics.conf
    ├── reddit-job-update_promos.conf
    ├── reddit-job-update_reddits.conf
    ├── reddit-job-update_sr_names.conf
    ├── reddit-job-update_trending_subreddits.conf
    └── reddit-paster.conf

================================================
FILE CONTENTS
================================================

================================================
FILE: .drone.yml
================================================
# This file is used by the Drone CI Server and Agents to determine what
# should happen (if anything) in response to git pushes and pull requests.
clone:
  depth: 50
  recursive: false
  tags: false
  # This is explicitly set so that forks retain a constant path.
  path: /drone/src/github.com/reddit/reddit-public

compose:
  # Some of these aren't actively used, but are required in that import
  # side-effects cause connections to services (regardless of whether
  # the services are interacted with in the tests). Each service is addressable
  # via 'localhost' through whatever ports the image exposes.
  postgres:
    # Temporarily needed for this image + Docker-in-Docker. Expected to
    # be removed when we upgrade to Drone 0.5.
    # https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
    privileged: true
    image: postgres:9.3.12
    environment:
      POSTGRES_USER: reddit
      POSTGRES_PASSWORD: password
      POSTGRES_DB: reddit

  cassandra:
    privileged: true
    # Cassandra 1.x is old enough not to have any official Docker images.
    # In the meantime, we've got a custom image configured specifically for r2:
    # https://github.com/reddit/docker-cassandra
    # We'll move over to the official images when we upgrade to C* 2.x.
    image: reddit/cassandra:single-1.2.19-v1

  rabbitmq:
    # NOTE: Using 3.4.x instead of 3.2.4 due to tag availability.
    image: rabbitmq:3.4
    environment:
      RABBITMQ_DEFAULT_VHOST: /
      RABBITMQ_DEFAULT_USER: reddit
      RABBITMQ_DEFAULT_PASS: reddit

  memcached:
    # NOTE: Using 1.4.21 instead of 1.4.17 due to tag availability.
    image: memcached:1.4.21

  zookeeper:
    image: jplock/zookeeper:3.4.6

# Build steps are where the setup, compilation, and tests happen.
build:
  # This is a fat Docker image with the apt dependencies pre-installed.
  # https://github.com/reddit/docker-reddit-py
  # Dependency changes in the install scripts can be optionally mirrored to the
  # image for speedups, but abstaining from doing so won't break the builds.
  image: reddit/reddit-py:latest
  # Always re-pull the image, since we're re-using the same tag.
  pull: true
  environment:
    DEBIAN_FRONTEND: noninteractive
  commands:
    # Prepares the environment for the test run.
    - install/drone.sh
    - cd r2
    - nosetests -v .
    - cd ..
    - ./scripts/stylecheck_git_diff.sh

# These plugins are triggered after a build failure/success.
notify:
  slack:
    webhook_url: $$CI_SLACK_WEBHOOK_URL
    channel: ci-notifications


================================================
FILE: .gitignore
================================================
*~
.*.sw?
*.pyc
*.pyo
.DS_Store

# mako
r2/data/
*.html.py

# build outputs
*.so
build/
dist/
r2/r2.egg-info/
r2/r2/lib/generated_strings.py

# ini files
*.ini
*.update

# static files
r2/r2/public/static/names.json
r2/r2/public/static/*.js
r2/r2/public/static/*.css
r2/r2/public/static/sprite*.png
r2/r2/public/static/*.gzip
r2/r2/public/static/js/lib/*.gzip

# cython
r2/r2/lib/mr_tools/_mr_tools.c
r2/r2/lib/db/_sorts.c
r2/r2/lib/sgm.c
r2/r2/lib/utils/_utils.c
r2/r2/lib/wrapped.c
r2/r2/lib/utils/comment_tree_utils.c

# vagrant
.vagrant/


================================================
FILE: .travis.yml
================================================
sudo: required
dist: trusty

language: python

python:
    - "2.7"

virtualenv:
    system_site_packages: true

services:
    - postgres
    - memcached
    - rabbitmq

install:
    - sudo install/travis.sh travis .

before_script:
    - install/setup_rabbitmq.sh
    - install/setup_postgres.sh
    - install/setup_cassandra.sh

script:
    - cd r2 && nosetests


================================================
FILE: LICENSE
================================================
reddit Inc.

Common Public Attribution License Version 1.0 (CPAL)

1. "Definitions"

1.0.1 "Commercial Use" means distribution or otherwise making the Covered Code
    available to a third party.

1.1 "Contributor" means each entity that creates or contributes to the creation
  of Modifications.

1.2 "Contributor Version" means the combination of the Original Code, prior
  Modifications used by a Contributor, and the Modifications made by that
  particular Contributor.

1.3 "Covered Code" means the Original Code or Modifications or the combination
  of the Original Code and Modifications, in each case including portions
  thereof.

1.4 "Electronic Distribution Mechanism" means a mechanism generally accepted in
  the software development community for the electronic transfer of data.

1.5 "Executable" means Covered Code in any form other than Source Code.

1.6 "Initial Developer" means the individual or entity identified as the Initial
  Developer in the Source Code notice required by Exhibit A.

1.7 "Larger Work" means a work which combines Covered Code or portions thereof
  with code not governed by the terms of this License.

1.8 "License" means this document.

1.8.1 "Licensable" means having the right to grant, to the maximum extent
    possible, whether at the time of the initial grant or subsequently acquired,
    any and all of the rights conveyed herein.

1.9 "Modifications" means any addition to or deletion from the substance or
  structure of either the Original Code or any previous Modifications. When
  Covered Code is released as a series of files, a Modification is:

A. Any addition to or deletion from the contents of a file containing Original
Code or previous Modifications.

B. Any new file that contains any part of the Original Code or previous
Modifications.

1.10 "Original Code" means Source Code of computer software code which is
  described in the Source Code notice required by Exhibit A as Original Code,
  and which, at the time of its release under this License is not already
  Covered Code governed by this License.

1.10.1 "Patent Claims" means any patent claim(s), now owned or hereafter
     acquired, including without limitation, method, process, and apparatus
     claims, in any patent Licensable by grantor.

1.11 "Source Code" means the preferred form of the Covered Code for making
  modifications to it, including all modules it contains, plus any associated
  interface definition files, scripts used to control compilation and
  installation of an Executable, or source code differential comparisons against
  either the Original Code or another well known, available Covered Code of the
  Contributor's choice. The Source Code can be in a compressed or archival form,
  provided the appropriate decompression or de-archiving software is widely
  available for no charge.

1.12 "You" (or "Your") means an individual or a legal entity exercising rights
  under, and complying with all of the terms of, this License or a future
  version of this License issued under Section 6.1. For legal entities, "You"
  includes any entity which controls, is controlled by, or is under common
  control with You. For purposes of this definition, "control" means (a) the
  power, direct or indirect, to cause the direction or management of such
  entity, whether by contract or otherwise, or (b) ownership of more than fifty
  percent (50%) of the outstanding shares or beneficial ownership of such
  entity.

2. Source Code License.

2.1 The Initial Developer Grant.

The Initial Developer hereby grants You a world-wide, royalty-free,
non-exclusive license, subject to third party intellectual property claims:

(a) under intellectual property rights (other than patent or trademark)
Licensable by Initial Developer to use, reproduce, modify, display, perform,
sublicense and distribute the Original Code (or portions thereof) with or
without Modifications, and/or as part of a Larger Work; and

(b) under Patents Claims infringed by the making, using or selling of Original
Code, to make, have made, use, practice, sell, and offer for sale, and/or
otherwise dispose of the Original Code (or portions thereof).

(c) the licenses granted in this Section 2.1(a) and (b) are effective on the
date Initial Developer first distributes Original Code under the terms of this
License.

(d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for
code that You delete from the Original Code; 2) separate from the Original Code;
or 3) for infringements caused by: i) the modification of the Original Code or
ii) the combination of the Original Code with other software or devices.

2.2 Contributor Grant.

Subject to third party intellectual property claims, each Contributor hereby
grants You a world-wide, royalty-free, non-exclusive license

(a) under intellectual property rights (other than patent or trademark)
Licensable by Contributor, to use, reproduce, modify, display, perform,
sublicense and distribute the Modifications created by such Contributor (or
portions thereof) either on an unmodified basis, with other Modifications, as
Covered Code and/or as part of a Larger Work; and

(b) under Patent Claims infringed by the making, using, or selling of
Modifications made by that Contributor either alone and/or in combination with
its Contributor Version (or portions of such combination), to make, use, sell,
offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by
that Contributor (or portions thereof); and 2) the combination of Modifications
made by that Contributor with its Contributor Version (or portions of such
combination).

(c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date
Contributor first makes Commercial Use of the Covered Code.

(d) Notwithstanding Section 2.2(b) above, no patent license is granted: 1) for
any code that Contributor has deleted from the Contributor Version; 2) separate
from the Contributor Version; 3) for infringements caused by: i) third party
modifications of Contributor Version or ii) the combination of Modifications
made by that Contributor with other software (except as part of the Contributor
Version) or other devices; or 4) under Patent Claims infringed by Covered Code
in the absence of Modifications made by that Contributor.

3. Distribution Obligations.

3.1 Application of License.

The Modifications which You create or to which You contribute are governed by
the terms of this License, including without limitation Section 2.2. The Source
Code version of Covered Code may be distributed only under the terms of this
License or a future version of this License released under Section 6.1, and You
must include a copy of this License with every copy of the Source Code You
distribute. You may not offer or impose any terms on any Source Code version
that alters or restricts the applicable version of this License or the
recipients' rights hereunder. However, You may include an additional document
offering the additional rights described in Section 3.5.

3.2 Availability of Source Code.

Any Modification which You create or to which You contribute must be made
available in Source Code form under the terms of this License either on the same
media as an Executable version or via an accepted Electronic Distribution
Mechanism to anyone to whom you made an Executable version available; and if
made available via Electronic Distribution Mechanism, must remain available for
at least twelve (12) months after the date it initially became available, or at
least six (6) months after a subsequent version of that particular Modification
has been made available to such recipients. You are responsible for ensuring
that the Source Code version remains available even if the Electronic
Distribution Mechanism is maintained by a third party.

3.3 Description of Modifications.

You must cause all Covered Code to which You contribute to contain a file
documenting the changes You made to create that Covered Code and the date of any
change. You must include a prominent statement that the Modification is derived,
directly or indirectly, from Original Code provided by the Initial Developer and
including the name of the Initial Developer in (a) the Source Code, and (b) in
any notice in an Executable version or related documentation in which You
describe the origin or ownership of the Covered Code.

3.4 Intellectual Property Matters

(a) Third Party Claims.

If Contributor has knowledge that a license under a third party's intellectual
property rights is required to exercise the rights granted by such Contributor
under Sections 2.1 or 2.2, Contributor must include a text file with the Source
Code distribution titled "LEGAL" which describes the claim and the party making
the claim in sufficient detail that a recipient will know whom to contact. If
Contributor obtains such knowledge after the Modification is made available as
described in Section 3.2, Contributor shall promptly modify the LEGAL file in
all copies Contributor makes available thereafter and shall take other steps
(such as notifying appropriate mailing lists or newsgroups) reasonably
calculated to inform those who received the Covered Code that new knowledge has
been obtained.

(b) Contributor APIs.

If Contributor's Modifications include an application programming interface and
Contributor has knowledge of patent licenses which are reasonably necessary to
implement that API, Contributor must also include this information in the LEGAL
file.

(c) Representations.

Contributor represents that, except as disclosed pursuant to Section 3.4(a)
above, Contributor believes that Contributor's Modifications are Contributor's
original creation(s) and/or Contributor has sufficient rights to grant the
rights conveyed by this License.

3.5 Required Notices.

You must duplicate the notice in Exhibit A in each file of the Source Code. If
it is not possible to put such notice in a particular Source Code file due to
its structure, then You must include such notice in a location (such as a
relevant directory) where a user would be likely to look for such a notice. If
You created one or more Modification(s) You may add your name as a Contributor
to the notice described in Exhibit A. You must also duplicate this License in
any documentation for the Source Code where You describe recipients' rights or
ownership rights relating to Covered Code. You may choose to offer, and to
charge a fee for, warranty, support, indemnity or liability obligations to one
or more recipients of Covered Code. However, You may do so only on Your own
behalf, and not on behalf of the Initial Developer or any Contributor. You must
make it absolutely clear than any such warranty, support, indemnity or liability
obligation is offered by You alone, and You hereby agree to indemnify the
Initial Developer and every Contributor for any liability incurred by the
Initial Developer or such Contributor as a result of warranty, support,
indemnity or liability terms You offer.

3.6 Distribution of Executable Versions.

You may distribute Covered Code in Executable form only if the requirements of
Section 3.1-3.5 have been met for that Covered Code, and if You include a notice
stating that the Source Code version of the Covered Code is available under the
terms of this License, including a description of how and where You have
fulfilled the obligations of Section 3.2. The notice must be conspicuously
included in any notice in an Executable version, related documentation or
collateral in which You describe recipients' rights relating to the Covered
Code. You may distribute the Executable version of Covered Code or ownership
rights under a license of Your choice, which may contain terms different from
this License, provided that You are in compliance with the terms of this License
and that the license for the Executable version does not attempt to limit or
alter the recipient's rights in the Source Code version from the rights set
forth in this License. If You distribute the Executable version under a
different license You must make it absolutely clear that any terms which differ
from this License are offered by You alone, not by the Initial Developer,
Original Developer or any Contributor. You hereby agree to indemnify the Initial
Developer, Original Developer and every Contributor for any liability incurred
by the Initial Developer, Original Developer or such Contributor as a result of
any such terms You offer.

3.7 Larger Works.

You may create a Larger Work by combining Covered Code with other code not
governed by the terms of this License and distribute the Larger Work as a single
product. In such a case, You must make sure the requirements of this License are
fulfilled for the Covered Code.

4. Inability to Comply Due to Statute or Regulation.

If it is impossible for You to comply with any of the terms of this License with
respect to some or all of the Covered Code due to statute, judicial order, or
regulation then You must: (a) comply with the terms of this License to the
maximum extent possible; and (b) describe the limitations and the code they
affect. Such description must be included in the LEGAL file described in Section
3.4 and must be included with all distributions of the Source Code. Except to
the extent prohibited by statute or regulation, such description must be
sufficiently detailed for a recipient of ordinary skill to be able to understand
it.

5. Application of this License.

This License applies to code to which the Initial Developer has attached the
notice in Exhibit A and to related Covered Code.

6. Versions of the License.

6.1 New Versions.

reddit Inc. ("reddit") may publish revised and/or new versions of the
License from time to time. Each version will be given a distinguishing version
number.

6.2 Effect of New Versions.

Once Covered Code has been published under a particular version of the License,
You may always continue to use it under the terms of that version. You may also
choose to use such Covered Code under the terms of any subsequent version of the
License published by reddit. No one other than reddit has the right to
modify the terms applicable to Covered Code created under this License.

6.3 Derivative Works.

If You create or use a modified version of this License (which you may only do
in order to apply it to code which is not already Covered Code governed by this
License), You must (a) rename Your license so that the phrases "reddit",
"CPAL" or any confusingly similar phrase do not appear in your license (except
to note that your license differs from this License) and (b) otherwise make it
clear that Your version of the license contains terms which differ from the
CPAL. (Filling in the name of the Initial Developer, Original Developer,
Original Code or Contributor in the notice described in Exhibit A shall not of
themselves be deemed to be modifications of this License.)

7. DISCLAIMER OF WARRANTY.

COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT
WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT
LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE,
FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE
QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE
PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER, ORIGINAL
DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING,
REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART
OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
THIS DISCLAIMER.

8. TERMINATION.

8.1 This License and the rights granted hereunder will terminate automatically
  if You fail to comply with terms herein and fail to cure such breach within 30
  days of becoming aware of the breach. All sublicenses to the Covered Code
  which are properly granted shall survive any termination of this
  License. Provisions which, by their nature, must remain in effect beyond the
  termination of this License shall survive.

8.2 If You initiate litigation by asserting a patent infringement claim
  (excluding declatory judgment actions) against Initial Developer, Original
  Developer or a Contributor (the Initial Developer, Original Developer or
  Contributor against whom You file such action is referred to as "Participant")
  alleging that:

(a) such Participant's Contributor Version directly or indirectly infringes any
patent, then any and all rights granted by such Participant to You under
Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from
Participant terminate prospectively, unless if within 60 days after receipt of
notice You either: (i) agree in writing to pay Participant a mutually agreeable
reasonable royalty for Your past and future use of Modifications made by such
Participant, or (ii) withdraw Your litigation claim with respect to the
Contributor Version against such Participant. If within 60 days of notice, a
reasonable royalty and payment arrangement are not mutually agreed upon in
writing by the parties or the litigation claim is not withdrawn, the rights
granted by Participant to You under Sections 2.1 and/or 2.2 automatically
terminate at the expiration of the 60 day notice period specified above.

(b) any software, hardware, or device, other than such Participant's Contributor
Version, directly or indirectly infringes any patent, then any rights granted to
You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective
as of the date You first made, used, sold, distributed, or had made,
Modifications made by that Participant.

8.3 If You assert a patent infringement claim against Participant alleging that
  such Participant's Contributor Version directly or indirectly infringes any
  patent where such claim is resolved (such as by license or settlement) prior
  to the initiation of patent infringement litigation, then the reasonable value
  of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be
  taken into account in determining the amount or value of any payment or
  license.

8.4 In the event of termination under Sections 8.1 or 8.2 above, all end user
  license agreements (excluding distributors and resellers) which have been
  validly granted by You or any distributor hereunder prior to termination shall
  survive termination.

9. LIMITATION OF LIABILITY.

UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING
NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ORIGINAL
DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY
SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT,
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING,
WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER
FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN
IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS
LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL
INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR
LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND
LIMITATION MAY NOT APPLY TO YOU.

10. U.S. GOVERNMENT END USERS.

The Covered Code is a "commercial item," as that term is defined in 48
C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and
"commercial computer software documentation," as such terms are used in 48
C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48
C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users
acquire Covered Code with only those rights set forth herein.

11. MISCELLANEOUS.

This License represents the complete agreement concerning subject matter
hereof. If any provision of this License is held to be unenforceable, such
provision shall be reformed only to the extent necessary to make it
enforceable. This License shall be governed by California law provisions (except
to the extent applicable law, if any, provides otherwise), excluding its
conflict-of-law provisions. With respect to disputes in which at least one party
is a citizen of, or an entity chartered or registered to do business in the
United States of America, any litigation relating to this License shall be
subject to the jurisdiction of the Federal Courts of the Northern District of
California, with venue lying in Santa Clara County, California, with the losing
party responsible for costs, including without limitation, court costs and
reasonable attorneys' fees and expenses. The application of the United Nations
Convention on Contracts for the International Sale of Goods is expressly
excluded. Any law or regulation which provides that the language of a contract
shall be construed against the drafter shall not apply to this License.

12. RESPONSIBILITY FOR CLAIMS.

As between Initial Developer, Original Developer and the Contributors, each
party is responsible for claims and damages arising, directly or indirectly, out
of its utilization of rights under this License and You agree to work with
Initial Developer, Original Developer and Contributors to distribute such
responsibility on an equitable basis. Nothing herein is intended or shall be
deemed to constitute any admission of liability.

13. MULTIPLE-LICENSED CODE.

Initial Developer may designate portions of the Covered Code as
Multiple-Licensed. Multiple-Licensed means that the Initial Developer permits
you to utilize portions of the Covered Code under Your choice of the CPAL or the
alternative licenses, if any, specified by the Initial Developer in the file
described in Exhibit A.

14. ADDITIONAL TERM: ATTRIBUTION

(a) As a modest attribution to the organizer of the development of the Original
Code ("Original Developer"), in the hope that its promotional value may help
justify the time, money and effort invested in writing the Original Code, the
Original Developer may include in Exhibit B ("Attribution Information") a
requirement that each time an Executable and Source Code or a Larger Work is
launched or initially run (which includes initiating a session), a prominent
display of the Original Developer's Attribution Information (as defined below)
must occur on the graphic user interface employed by the end user to access such
Covered Code (which may include display on a splash screen), if any. The size of
the graphic image should be consistent with the size of the other elements of
the Attribution Information. If the access by the end user to the Executable and
Source Code does not create a graphic user interface for access to the Covered
Code, this obligation shall not apply. If the Original Code displays such
Attribution Information in a particular form (such as in the form of a splash
screen, notice at login, an "about" display, or dedicated attribution area on
user interface screens), continued use of such form for that Attribution
Information is one way of meeting this requirement for notice.

(b) Attribution information may only include a copyright notice, a brief phrase,
graphic image and a URL ("Attribution Information") and is subject to the
Attribution Limits as defined below. For these purposes, prominent shall mean
display for sufficient duration to give reasonable notice to the user of the
identity of the Original Developer and that if You include Attribution
Information or similar information for other parties, You must ensure that the
Attribution Information for the Original Developer shall be no less prominent
than such Attribution Information or similar information for the other
party. For greater certainty, the Original Developer may choose to specify in
Exhibit B below that the above attribution requirement only applies to an
Executable and Source Code resulting from the Original Code or any Modification,
but not a Larger Work. The intent is to provide for reasonably modest
attribution, therefore the Original Developer cannot require that You display,
at any time, more than the following information as Attribution Information: (a)
a copyright notice including the name of the Original Developer; (b) a word or
one phrase (not exceeding 10 words); (c) one graphic image provided by the
Original Developer; and (d) a URL (collectively, the "Attribution Limits").

(c) If Exhibit B does not include any Attribution Information, then there are no
requirements for You to display any Attribution Information of the Original
Developer.

(d) You acknowledge that all trademarks, service marks and/or trade names
contained within the Attribution Information distributed with the Covered Code
are the exclusive property of their owners and may only be used with the
permission of their owners, or under circumstances otherwise permitted by law or
as expressly set out in this License.

15. ADDITIONAL TERM: NETWORK USE.

The term "External Deployment" means the use, distribution, or communication of
the Original Code or Modifications in any way such that the Original Code or
Modifications may be used by anyone other than You, whether those works are
distributed or communicated to those persons or made available as an application
intended for use over a network. As an express condition for the grants of
license hereunder, You must treat any External Deployment by You of the Original
Code or Modifications as a distribution under section 3.1 and make Source Code
available under Section 3.2.

EXHIBIT A. Common Public Attribution License Version 1.0.

"The contents of this file are subject to the Common Public Attribution License
Version 1.0. (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
License Version 1.1, but Sections 14 and 15 have been added to cover use of
software over a computer network and provide for limited attribution for the
Original Developer. In addition, Exhibit A has been modified to be consistent
with Exhibit B.

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
specific language governing rights and limitations under the License.

The Original Code is reddit.

The Original Developer is the Initial Developer.  The Initial Developer of the
Original Code is reddit Inc.

All portions of the code written by reddit are Copyright (c) 2006-2015
reddit Inc. All Rights Reserved.

EXHIBIT B. Attribution Information

Attribution Copyright Notice: Copyright (c) 2006-2015 reddit Inc. All Rights
Reserved.

Attribution Phrase (not exceeding 10 words): Powered by reddit

Attribution URL: http://code.reddit.com

Graphic Image as provided in the Covered Code:
http://code.reddit.com/reddit_logo.png

Display of Attribution Information is required in Larger Works which are defined
in the CPAL as a work which combines Covered Code or portions thereof with code
not governed by the terms of the CPAL.


================================================
FILE: README.md
================================================
## This repository is archived.

This repository is archived and will not receive any updates or accept issues or pull requests.

To report bugs in reddit.com please make a post in [/r/bugs](http://www.reddit.com/r/bugs).

If you have found a bug that can in some way compromise the security of the
site or its users, please exercise [responsible
disclosure](http://www.reddit.com/wiki/whitehat) and e-mail
security@reddit.com.

---

### API

For notices about reddit API changes and discussion of reddit API client development, subscribe to the [/r/redditdev](http://www.reddit.com/r/redditdev) and [/r/changelog](http://www.reddit.com/r/changelog) subreddits.

To learn more about reddit's API, check out our [automated API documentation](http://www.reddit.com/dev/api) and the [API wiki page](https://github.com/reddit/reddit/wiki/API). Please use a unique User-Agent string and take care to abide by our [API rules](https://github.com/reddit/reddit/wiki/API#wiki-rules).

### Quickstart

To set up your own instance of reddit see the [install guide](https://github.com/reddit/reddit/wiki/Install-guide).


================================================
FILE: SECURITY.md
================================================
![white hat trophy](https://b.thumbs.redditmedia.com/n0_7BYpCg_RYB1j7.png)

Like all pieces of software, reddit has bugs – and it always will. Some
of them will take the form of security vulnerabilities.

If you find a security vulnerability in reddit, please privately report it to
[security@reddit.com](mailto:security@reddit.com). We'll get back to you ASAP,
usually within 24 hours.

Once the issue is fixed, if you provide your reddit username, we'll credit your
account with a [whitehat](https://www.reddit.com/wiki/whitehat) trophy.

Thank you and good hunting.


================================================
FILE: Vagrantfile
================================================
# -*- mode: ruby -*-
# vi: set ft=ruby :

# This assumes that the host machine has r2 and all the reddit plugins checked
# out and in the correct directories--pay attention to both name and position
# relative to the r2 code:
#
# r2:         {ROOTDIR}/reddit
#
# plugins:
# about:      {ROOTDIR}/about
# gold:       {ROOTDIR}/gold
#
# All plugins are optional. A plugin will only be installed if it is listed
# in `plugins` AND it is located in a directory that both follows the plugin
# naming convention and is correctly located on the host machine. The general
# rule for naming each plugin directory is that "reddit-plugin-NAME" should be
# in the directory {ROOTDIR}/NAME.
#
# This VagrantFile allows for the creation of two VMs:
#   * default: the primary VM, with all services necessary to run reddit
#              locally against the local codebase.
#   * travis:  Testing-only VM suitable for running `nosetests` and debugging
#              issues encountered without having to wait for travis-ci to pick
#              up the build.  This will *not* be the same environment as
#              travis, but it should be useful for repairing broken tests.
#
# To start your vagrant box simply enter `vagrant up` from {ROOTDIR}/reddit.
# You can then ssh into it with `vagrant ssh`.
#
# avahi-daemon is installed on the guest VM so you can access your local install
# at https://reddit.local. If that fails you'll need to update your host
# machine's hosts file (/etc/hosts) to include the line:
# 192.168.56.111 reddit.local
#
# If you want to create additional vagrant boxes you can copy this file
# elsewhere, but be sure to update `code_share_host_path` to be the absolute
# path to {ROOTDIR}.

vagrant_user = "vagrant"

# code directories
this_path = File.absolute_path(__FILE__)
reddit_dir = File.expand_path("..", this_path)
code_share_host_path = File.expand_path("..", reddit_dir)
code_share_guest_path = "/media/reddit_code"
plugins = [
  "about",
  "gold",
]

# overlayfs directories
overlay_mount = "/home/#{vagrant_user}/src"
overlay_lower = code_share_guest_path
overlay_upper = "/home/#{vagrant_user}/.overlay"

# "default" vm config
guest_ip = "192.168.56.111"
guest_mem = "4096"
guest_swap = "4096"
hostname = "reddit.local"


Vagrant.configure(2) do |config|
  config.vm.box = "trusty-cloud-image"
  config.vm.box_url = "https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box"

  # mount the host shared folder
  config.vm.synced_folder code_share_host_path, code_share_guest_path, mount_options: ["ro"]

  config.vm.provider "virtualbox" do |vb|
    vb.memory = guest_mem
  end

  # ubuntu cloud image has no swapfile by default, set one up
  config.vm.provision "shell", inline: <<-SCRIPT
    if ! grep -q swapfile /etc/fstab; then
      echo 'swapfile not found. Adding swapfile.'
      fallocate -l #{guest_swap}M /swapfile
      chmod 600 /swapfile
      mkswap /swapfile
      swapon /swapfile
      echo '/swapfile none swap defaults 0 0' >> /etc/fstab
    else
      echo 'swapfile found. No changes made.'
    fi
  SCRIPT

  # set up the overlay filesystem
  config.vm.provision "shell", inline: <<-SCRIPT
    if [ ! -d #{overlay_mount} ]; then
      echo "creating overlay mount directory #{overlay_mount}"
      sudo -u #{vagrant_user} mkdir #{overlay_mount}
    fi

    if [ ! -d #{overlay_upper} ]; then
      echo "creating overlay upper directory #{overlay_upper}"
      sudo -u #{vagrant_user} mkdir #{overlay_upper}
    fi

    echo "mounting overlayfs (lower: #{overlay_lower}, upper: #{overlay_upper}, mount: #{overlay_mount})"
    mount -t overlayfs overlayfs -o lowerdir=#{overlay_lower},upperdir=#{overlay_upper} #{overlay_mount}
  SCRIPT

  # NOTE: This VM exists solely to assist in writing tests.  It does not actually
  # install travis but rather builds a minimal vm with only the services
  # available under a travis build to aid in test debugging (via `nosetests`)
  # To use:
  #     $ vagrant up travis
  #     $ vagrant ssh travis
  #     vagrant@travis$ cd src/reddit/r2 && nosetests
  config.vm.define "travis", autostart: false do |travis|
      travis.vm.hostname = "travis"
      # run install script
      travis.vm.provision "shell", inline: <<-SCRIPT
        if [ ! -f /var/local/reddit_installed ]; then
          echo "running install script"
          cd /home/#{vagrant_user}/src/reddit
          ./install/travis.sh vagrant
          touch /var/local/reddit_installed
        else
          echo "install script already run"
        fi
      SCRIPT
  end

  # NB: this is the primary VM. To build run
  #    $ vagrant up
  # [though 'vagrant up default' will also work, the 'default' is redudnant]
  # Once built, avahi-daemon should guarantee the instance will be accessible
  # from https://reddit.local/
  config.vm.define "default", primary: true do |redditlocal|
      redditlocal.vm.hostname = hostname
      # host-only network interface
      redditlocal.vm.network "private_network", ip: guest_ip

      # rabbitmq web interface
      config.vm.network "forwarded_port", guest: 15672, host: 15672

      # run install script
      plugin_string = plugins.join(" ")
      redditlocal.vm.provision "shell", inline: <<-SCRIPT
        if [ ! -f /var/local/reddit_installed ]; then
          echo "running install script"
          cd /home/#{vagrant_user}/src/reddit
          REDDIT_PLUGINS="#{plugin_string}" REDDIT_DOMAIN="#{hostname}" ./install/reddit.sh
          touch /var/local/reddit_installed
        else
          echo "install script already run"
        fi
      SCRIPT

      # inject test data
      redditlocal.vm.provision "shell", inline: <<-SCRIPT
        if [ ! -f /var/local/test_data_injected ]; then
          cd /home/#{vagrant_user}/src/reddit
          sudo -u #{vagrant_user} reddit-run scripts/inject_test_data.py -c 'inject_test_data()'
          touch /var/local/test_data_injected
        else
          echo "inject test data already run"
        fi

        # HACK: stop and start everything (otherwise sometimes there's an issue with
        # ports being in use?)
        reddit-stop
        reddit-start
      SCRIPT

      # additional setup
      redditlocal.vm.provision "shell", inline: <<-SCRIPT
        if [ ! -f /var/local/additional_setup ]; then
          apt-get install -y ipython avahi-daemon
          touch /var/local/additional_setup
        else
          echo "additional setup already run"
        fi
      SCRIPT

      # DONE: let this run whenever provision is run so that the user can see
      # how to proceed.
      redditlocal.vm.provision "shell", inline: <<-SCRIPT
        cd /home/#{vagrant_user}/src/reddit
        REDDIT_DOMAIN="#{hostname}" ./install/done.sh
      SCRIPT
  end
end


================================================
FILE: install/README.md
================================================
# Reddit installer development instructions

This folder contains all of the installation scripts required to build reddit on a stock Ubuntu 14.04 (trusty) box.  Originally all of this was included in `../install-reddit.sh` but the need to fork the image into an base installer for testing as well as a full installer for local use meant some reorganization and fracturing of the original script.

When making updates to any of these files, Travis will test out the minimal install in `travis.sh` but not the full install.  *Please test on a fresh VM, and preferably using a modified web-install instructions:*

```bash
wget https://raw.github.com/$GITHUBUSER/reddit/$TESTBRANCH/install-reddit.sh
chmod +x install-reddit.sh
./install-reddit.sh
```

This will ensure that the installation is tested in the most basic installation scenario.

**NOTE** if you find yourself adding files to this folder, please update `$NEEDED` in `../install-reddit.sh` to reflect the addition.  


================================================
FILE: install/done.sh
================================================
#!/bin/bash
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

# load configuration
RUNDIR=$(dirname $0)
source $RUNDIR/install.cfg

###############################################################################
# All done!
###############################################################################
cat <<CONCLUSION

Congratulations! reddit is now installed.

The reddit application code is managed with upstart, to see what's currently
running, run

    sudo initctl list | grep reddit

Cron jobs start with "reddit-job-" and queue processors start with
"reddit-consumer-". The crons are managed by /etc/cron.d/reddit. You can
initiate a restart of all the consumers by running:

    sudo reddit-restart

or target specific ones:

    sudo reddit-restart scraper_q

See the GitHub wiki for more information on these jobs:

* https://github.com/reddit/reddit/wiki/Cron-jobs
* https://github.com/reddit/reddit/wiki/Services

The reddit code can be shut down or started up with

    sudo reddit-stop
    sudo reddit-start

And if you think caching might be hurting you, you can flush memcache with

    reddit-flush

Now that the core of reddit is installed, you may want to do some additional
steps:

* Ensure that $REDDIT_DOMAIN resolves to this machine.

* To populate the database with test data, run:

    cd $REDDIT_SRC/reddit
    reddit-run scripts/inject_test_data.py -c 'inject_test_data()'

* Manually run reddit-job-update_reddits immediately after populating the db
  or adding your own subreddits.
CONCLUSION


================================================
FILE: install/drone.sh
================================================
#!/usr/bin/env bash
###############################################################################
# reddit Drone environment installer
# ----------------------------------
# This script re-purposes some of our existing vagrant/Travis install and
# setup scripts for our Drone CI builds.
#
# NOTE: You don't want to run this script directly in your development
# environment, since we assume that it's running within this Docker image
# that Drone runs our builds within: https://github.com/reddit/docker-reddit-py
#
# docker-reddit-py has most of the apt dependencies pre-installed in order to
# significantly reduce our build times.
#
# Refer to .drone.yml in the repo root to see where this script gets called
# during a build.
###############################################################################

###############################################################################
# Install prerequisites
###############################################################################

# Under normal operation, this won't install anything new. We're re-using the
# logic that checks to make sure all services have finished starting before
# continuing.
install/install_services.sh

###############################################################################
# Install and configure the reddit code
###############################################################################

pushd r2
python setup.py develop
make pyx
ln -sf example.ini test.ini
popd

###############################################################################
# Configure local services
###############################################################################

# Creates the column families required for the tests
install/setup_cassandra.sh


================================================
FILE: install/install.cfg
================================================
#!/bin/bash
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################
set -e -x

###############################################################################
# Configuration
###############################################################################
# which user to install the code for; defaults to the user invoking this script
REDDIT_USER=${REDDIT_USER:-$SUDO_USER}

# the group to run reddit code as; must exist already
REDDIT_GROUP=${REDDIT_GROUP:-nogroup}

# the root directory to base the install in. must exist already
REDDIT_HOME=${REDDIT_HOME:-/home/$REDDIT_USER}
REDDIT_SRC=${REDDIT_SRC:-$REDDIT_HOME/src}

# the domain that you will connect to your reddit install with.
# MUST contain a . in it somewhere as browsers won't do cookies for dotless
# domains. an IP address will suffice if nothing else is available.
REDDIT_DOMAIN=${REDDIT_DOMAIN:-reddit.local}

# the plugins to install if they adhere to plugin naming and location
# conventions on the host
REDDIT_PLUGINS=${REDDIT_PLUGINS:-about gold}

# aptitude configuration
APTITUDE_OPTIONS=${APTITUDE_OPTIONS:-"-y"}

# Custom datastax repo
CASSANDRA_SOURCES_LIST=/etc/apt/sources.list.d/cassandra.sources.list

export DEBIAN_FRONTEND=noninteractive


================================================
FILE: install/install_apt.sh
================================================
#!/bin/bash
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

# load configuration
RUNDIR=$(dirname $0)
source $RUNDIR/install.cfg

# run an aptitude update to make sure python-software-properties
# dependencies are found
apt-get update

# add the datastax cassandra repos (NB: this is required for
# install_cassandra.sh to work correctly, and the non-existence of this
# file will trigger install_cassandra.sh to rerun this script)
echo deb http://debian.datastax.com/community stable main | \
    sudo tee $CASSANDRA_SOURCES_LIST
    
wget -qO- -L https://debian.datastax.com/debian/repo_key | \
    sudo apt-key add -

# add the reddit ppa for some custom packages
apt-get install $APTITUDE_OPTIONS python-software-properties
apt-add-repository -y ppa:reddit/ppa

# pin the ppa -- packages present in the ppa will take precedence over
# ones in other repositories (unless further pinning is done)
cat <<HERE > /etc/apt/preferences.d/reddit
Package: *
Pin: release o=LP-PPA-reddit
Pin-Priority: 600
HERE

# grab the new ppas' package listings
apt-get update

# travis gives us a stock libmemcached.  We have to remove that
apt-get remove $APTITUDE_OPTIONS $(dpkg-query  -W -f='${binary:Package}\n' | grep libmemcached | tr '\n' ' ')
apt-get autoremove $APTITUDE_OPTIONS

# install prerequisites
cat <<PACKAGES | xargs apt-get install $APTITUDE_OPTIONS
netcat-openbsd
git-core

python-dev
python-setuptools
python-routes
python-pylons
python-boto
python-tz
python-crypto
python-babel
python-numpy
python-dateutil
cython
python-sqlalchemy
python-beautifulsoup
python-chardet
python-psycopg2
python-pycassa
python-imaging
python-pycaptcha
python-pylibmc=1.2.2-1~trusty5
python-amqplib
python-bcrypt
python-snappy
python-snudown
python-l2cs
python-lxml
python-kazoo
python-stripe
python-tinycss2
python-unidecode
python-mock
python-yaml
python-httpagentparser

python-baseplate

python-flask
geoip-bin
geoip-database
python-geoip

nodejs
node-less
node-uglify
gettext
make
optipng
jpegoptim

libpcre3-dev

python-gevent
python-gevent-websocket
python-haigha

python-redis
python-pyramid
python-raven
PACKAGES


================================================
FILE: install/install_cassandra.sh
================================================
#!/bin/bash
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

# load configuration
RUNDIR=$(dirname $0)
source $RUNDIR/install.cfg

if [ ! -e $CASSANDRA_SOURCES_LIST ]; then
    echo "Cassandra repo not added.  Running `install_apt.sh`"
    $RUNDIR/install_apt.sh
fi

# install cassandra
sudo apt-get install $APTITUDE_OPTIONS cassandra=1.2.19

# we don't want to upgrade to C* 2.0 yet, so we'll put it on hold
apt-mark hold cassandra || true

# cassandra doesn't auto-start after install
sudo service cassandra start

# check each port for connectivity
echo "Waiting for cassandra to be available..."
while ! nc -vz localhost 9160; do
    sleep 1
done


================================================
FILE: install/install_services.sh
================================================
#!/bin/bash
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

###############################################################################
# Install services
###############################################################################

# load configuration
RUNDIR=$(dirname $0)
source $RUNDIR/install.cfg

# install prerequisites
cat <<PACKAGES | xargs apt-get install $APTITUDE_OPTIONS
mcrouter
memcached
postgresql
postgresql-client
rabbitmq-server
haproxy
nginx
gunicorn
redis-server
PACKAGES

###############################################################################
# Wait for all the services to be up
###############################################################################
# check each port for connectivity
echo "Waiting for services to be available, see source for port meanings..."
# 11211 - memcache
# 5432 - postgres
# 5672 - rabbitmq
for port in 11211 5432 5672; do
    while ! nc -vz localhost $port; do
        sleep 1
    done
done


================================================
FILE: install/install_zookeeper.sh
================================================
#!/bin/bash
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2016 reddit
# Inc. All Rights Reserved.
###############################################################################

RUNDIR=$(dirname $0)
source $RUNDIR/install.cfg

sudo apt-get install $APTITUDE_OPTIONS zookeeperd

echo "Waiting for ZooKeeper to be available..."
while ! nc -vz localhost 2181; do
    sleep 1
done


================================================
FILE: install/reddit.sh
================================================
#!/bin/bash
###############################################################################
# reddit dev environment installer
# --------------------------------
# This script installs a reddit stack suitable for development. DO NOT run this
# on a system that you use for other purposes as it might delete important
# files, truncate your databases, and otherwise do mean things to you.
#
# By default, this script will install the reddit code in the current user's
# home directory and all of its dependencies (including libraries and database
# servers) at the system level. The installed reddit will expect to be visited
# on the domain "reddit.local" unless specified otherwise.  Configuring name
# resolution for the domain is expected to be done outside the installed
# environment (e.g. in your host machine's /etc/hosts file) and is not
# something this script handles.
#
# Several configuration options (listed in the "Configuration" section below)
# are overridable with environment variables. e.g.
#
#    sudo REDDIT_DOMAIN=example.com ./install/reddit.sh
#
###############################################################################

# load configuration
RUNDIR=$(dirname $0)
source $RUNDIR/install.cfg


###############################################################################
# Sanity Checks
###############################################################################
if [[ $EUID -ne 0 ]]; then
    echo "ERROR: Must be run with root privileges."
    exit 1
fi

if [[ -z "$REDDIT_USER" ]]; then
    # in a production install, you'd want the code to be owned by root and run
    # by a less privileged user. this script is intended to build a development
    # install, so we expect the owner to run the app and not be root.
    cat <<END
ERROR: You have not specified a user. This usually means you're running this
script directly as root. It is not recommended to run reddit as the root user.

Please create a user to run reddit and set the REDDIT_USER variable
appropriately.
END
    exit 1
fi

if [[ "amd64" != $(dpkg --print-architecture) ]]; then
    cat <<END
ERROR: This host is running the $(dpkg --print-architecture) architecture!

Because of the pre-built dependencies in our PPA, and some extra picky things
like ID generation in liveupdate, installing reddit is only supported on amd64
architectures.
END
    exit 1
fi

# seriously! these checks are here for a reason. the packages from the
# reddit ppa aren't built for anything but trusty (14.04) right now, so
# if you try and use this install script on another release you're gonna
# have a bad time.
source /etc/lsb-release
if [ "$DISTRIB_ID" != "Ubuntu" -o "$DISTRIB_RELEASE" != "14.04" ]; then
    echo "ERROR: Only Ubuntu 14.04 is supported."
    exit 1
fi

if [[ "2000000" -gt $(awk '/MemTotal/{print $2}' /proc/meminfo) ]]; then
    LOW_MEM_PROMPT="reddit requires at least 2GB of memory to work properly, continue anyway? [y/n] "
    read -er -n1 -p "$LOW_MEM_PROMPT" response
    if [[ "$response" != "y" ]]; then
      echo "Quitting."
      exit 1
    fi
fi

REDDIT_AVAILABLE_PLUGINS=""
for plugin in $REDDIT_PLUGINS; do
    if [ -d $REDDIT_SRC/$plugin ]; then
        if [[ -z "$REDDIT_PLUGINS" ]]; then
            REDDIT_AVAILABLE_PLUGINS+="$plugin"
        else
            REDDIT_AVAILABLE_PLUGINS+=" $plugin"
        fi
        echo "plugin $plugin found"
    else
        echo "plugin $plugin not found"
    fi
done

###############################################################################
# Install prerequisites
###############################################################################

# install primary packages
$RUNDIR/install_apt.sh

# install cassandra from datastax
$RUNDIR/install_cassandra.sh

# install zookeeper
$RUNDIR/install_zookeeper.sh

# install services (rabbitmq, postgres, memcached, etc.)
$RUNDIR/install_services.sh

###############################################################################
# Install the reddit source repositories
###############################################################################
if [ ! -d $REDDIT_SRC ]; then
    mkdir -p $REDDIT_SRC
    chown $REDDIT_USER $REDDIT_SRC
fi

function copy_upstart {
    if [ -d ${1}/upstart ]; then
        cp ${1}/upstart/* /etc/init/
    fi
}

function clone_reddit_repo {
    local destination=$REDDIT_SRC/${1}
    local repository_url=https://github.com/${2}.git

    if [ ! -d $destination ]; then
        sudo -u $REDDIT_USER -H git clone $repository_url $destination
    fi

    copy_upstart $destination
}

function clone_reddit_service_repo {
    clone_reddit_repo $1 reddit/reddit-service-$1
}

clone_reddit_repo reddit reddit/reddit
clone_reddit_repo i18n reddit/reddit-i18n
clone_reddit_service_repo websockets
clone_reddit_service_repo activity

###############################################################################
# Configure Services
###############################################################################

# Configure Cassandra
$RUNDIR/setup_cassandra.sh

# Configure PostgreSQL
$RUNDIR/setup_postgres.sh

# Configure mcrouter
$RUNDIR/setup_mcrouter.sh

# Configure RabbitMQ
$RUNDIR/setup_rabbitmq.sh

###############################################################################
# Install and configure the reddit code
###############################################################################
function install_reddit_repo {
    pushd $REDDIT_SRC/$1
    sudo -u $REDDIT_USER python setup.py build
    python setup.py develop --no-deps
    popd
}

install_reddit_repo reddit/r2
install_reddit_repo i18n
for plugin in $REDDIT_AVAILABLE_PLUGINS; do
    copy_upstart $REDDIT_SRC/$plugin
    install_reddit_repo $plugin
done
install_reddit_repo websockets
install_reddit_repo activity

# generate binary translation files from source
sudo -u $REDDIT_USER make -C $REDDIT_SRC/i18n clean all

# this builds static files and should be run *after* languages are installed
# so that the proper language-specific static files can be generated and after
# plugins are installed so all the static files are available.
pushd $REDDIT_SRC/reddit/r2
sudo -u $REDDIT_USER make clean pyx

plugin_str=$(echo -n "$REDDIT_AVAILABLE_PLUGINS" | tr " " ,)
if [ ! -f development.update ]; then
    cat > development.update <<DEVELOPMENT
# after editing this file, run "make ini" to
# generate a new development.ini

[DEFAULT]
# global debug flag -- displays pylons stacktrace rather than 500 page on error when true
# WARNING: a pylons stacktrace allows remote code execution. Make sure this is false
# if your server is publicly accessible.
debug = true

disable_ads = true
disable_captcha = true
disable_ratelimit = true
disable_require_admin_otp = true

domain = $REDDIT_DOMAIN
oauth_domain = $REDDIT_DOMAIN

plugins = $plugin_str

media_provider = filesystem
media_fs_root = /srv/www/media
media_fs_base_url_http = http://%(domain)s/media/

[server:main]
port = 8001
DEVELOPMENT
    chown $REDDIT_USER development.update
else
    sed -i "s/^plugins = .*$/plugins = $plugin_str/" $REDDIT_SRC/reddit/r2/development.update
    sed -i "s/^domain = .*$/domain = $REDDIT_DOMAIN/" $REDDIT_SRC/reddit/r2/development.update
    sed -i "s/^oauth_domain = .*$/oauth_domain = $REDDIT_DOMAIN/" $REDDIT_SRC/reddit/r2/development.update
fi

sudo -u $REDDIT_USER make ini

if [ ! -L run.ini ]; then
    sudo -u $REDDIT_USER ln -nsf development.ini run.ini
fi

popd

###############################################################################
# some useful helper scripts
###############################################################################
function helper-script() {
    cat > $1
    chmod 755 $1
}

helper-script /usr/local/bin/reddit-run <<REDDITRUN
#!/bin/bash
exec paster --plugin=r2 run $REDDIT_SRC/reddit/r2/run.ini "\$@"
REDDITRUN

helper-script /usr/local/bin/reddit-shell <<REDDITSHELL
#!/bin/bash
exec paster --plugin=r2 shell $REDDIT_SRC/reddit/r2/run.ini
REDDITSHELL

helper-script /usr/local/bin/reddit-start <<REDDITSTART
#!/bin/bash
initctl emit reddit-start
REDDITSTART

helper-script /usr/local/bin/reddit-stop <<REDDITSTOP
#!/bin/bash
initctl emit reddit-stop
REDDITSTOP

helper-script /usr/local/bin/reddit-restart <<REDDITRESTART
#!/bin/bash
initctl emit reddit-restart TARGET=${1:-all}
REDDITRESTART

helper-script /usr/local/bin/reddit-flush <<REDDITFLUSH
#!/bin/bash
echo flush_all | nc localhost 11211
REDDITFLUSH

helper-script /usr/local/bin/reddit-serve <<REDDITSERVE
#!/bin/bash
exec paster serve --reload $REDDIT_SRC/reddit/r2/run.ini
REDDITSERVE

###############################################################################
# pixel and click server
###############################################################################
mkdir -p /var/opt/reddit/
chown $REDDIT_USER:$REDDIT_GROUP /var/opt/reddit/

mkdir -p /srv/www/pixel
chown $REDDIT_USER:$REDDIT_GROUP /srv/www/pixel
cp $REDDIT_SRC/reddit/r2/r2/public/static/pixel.png /srv/www/pixel

if [ ! -f /etc/gunicorn.d/click.conf ]; then
    cat > /etc/gunicorn.d/click.conf <<CLICK
CONFIG = {
    "mode": "wsgi",
    "working_dir": "$REDDIT_SRC/reddit/scripts",
    "user": "$REDDIT_USER",
    "group": "$REDDIT_USER",
    "args": (
        "--bind=unix:/var/opt/reddit/click.sock",
        "--workers=1",
        "tracker:application",
    ),
}
CLICK
fi

service gunicorn start

###############################################################################
# nginx
###############################################################################

mkdir -p /srv/www/media
chown $REDDIT_USER:$REDDIT_GROUP /srv/www/media

cat > /etc/nginx/sites-available/reddit-media <<MEDIA
server {
    listen 9000;

    expires max;

    location /media/ {
        alias /srv/www/media/;
    }
}
MEDIA

cat > /etc/nginx/sites-available/reddit-pixel <<PIXEL
upstream click_server {
  server unix:/var/opt/reddit/click.sock fail_timeout=0;
}

server {
  listen 8082;

  log_format directlog '\$remote_addr - \$remote_user [\$time_local] '
                      '"\$request_method \$request_uri \$server_protocol" \$status \$body_bytes_sent '
                      '"\$http_referer" "\$http_user_agent"';
  access_log      /var/log/nginx/traffic/traffic.log directlog;

  location / {

    rewrite ^/pixel/of_ /pixel.png;

    add_header Last-Modified "";
    add_header Pragma "no-cache";

    expires -1;
    root /srv/www/pixel/;
  }

  location /click {
    proxy_pass http://click_server;
  }
}
PIXEL

cat > /etc/nginx/sites-available/reddit-ssl <<SSL
map \$http_upgrade \$connection_upgrade {
  default upgrade;
  ''      close;
}

server {
    listen 443;

    ssl on;
    ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
    ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
    ssl_prefer_server_ciphers on;

    ssl_session_cache shared:SSL:1m;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host \$http_host;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For \$remote_addr;
        proxy_pass_header Server;

        # allow websockets through if desired
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \$connection_upgrade;
    }
}
SSL

# remove the default nginx site that may conflict with haproxy
rm -rf /etc/nginx/sites-enabled/default
# put our config in place
ln -nsf /etc/nginx/sites-available/reddit-media /etc/nginx/sites-enabled/
ln -nsf /etc/nginx/sites-available/reddit-pixel /etc/nginx/sites-enabled/
ln -nsf /etc/nginx/sites-available/reddit-ssl /etc/nginx/sites-enabled/

# make the pixel log directory
mkdir -p /var/log/nginx/traffic

# link the ini file for the Flask click tracker
ln -nsf $REDDIT_SRC/reddit/r2/development.ini $REDDIT_SRC/reddit/scripts/production.ini

service nginx restart

###############################################################################
# haproxy
###############################################################################
if [ -e /etc/haproxy/haproxy.cfg ]; then
    BACKUP_HAPROXY=$(mktemp /etc/haproxy/haproxy.cfg.XXX)
    echo "Backing up /etc/haproxy/haproxy.cfg to $BACKUP_HAPROXY"
    cat /etc/haproxy/haproxy.cfg > $BACKUP_HAPROXY
fi

# make sure haproxy is enabled
cat > /etc/default/haproxy <<DEFAULT
ENABLED=1
DEFAULT

# configure haproxy
cat > /etc/haproxy/haproxy.cfg <<HAPROXY
global
    maxconn 350

frontend frontend
    mode http

    bind 0.0.0.0:80
    bind 127.0.0.1:8080

    timeout client 24h
    option forwardfor except 127.0.0.1
    option httpclose

    # make sure that requests have x-forwarded-proto: https iff tls
    reqidel ^X-Forwarded-Proto:.*
    acl is-ssl dst_port 8080
    reqadd X-Forwarded-Proto:\ https if is-ssl

    # send websockets to the websocket service
    acl is-websocket hdr(Upgrade) -i WebSocket
    use_backend websockets if is-websocket

    # send media stuff to the local nginx
    acl is-media path_beg /media/
    use_backend media if is-media

    # send pixel stuff to local nginx
    acl is-pixel path_beg /pixel/
    acl is-click path_beg /click
    use_backend pixel if is-pixel || is-click

    default_backend reddit

backend reddit
    mode http
    timeout connect 4000
    timeout server 30000
    timeout queue 60000
    balance roundrobin

    server app01-8001 localhost:8001 maxconn 30

backend websockets
    mode http
    timeout connect 4s
    timeout server 24h
    balance roundrobin

    server websockets localhost:9001 maxconn 250

backend media
    mode http
    timeout connect 4000
    timeout server 30000
    timeout queue 60000
    balance roundrobin

    server nginx localhost:9000 maxconn 20

backend pixel
    mode http
    timeout connect 4000
    timeout server 30000
    timeout queue 60000
    balance roundrobin

    server nginx localhost:8082 maxconn 20
HAPROXY

# this will start it even if currently stopped
service haproxy restart

###############################################################################
# websocket service
###############################################################################

if [ ! -f /etc/init/reddit-websockets.conf ]; then
    cat > /etc/init/reddit-websockets.conf << UPSTART_WEBSOCKETS
description "websockets service"

stop on runlevel [!2345] or reddit-restart all or reddit-restart websockets
start on runlevel [2345] or reddit-restart all or reddit-restart websockets

respawn
respawn limit 10 5
kill timeout 15

limit nofile 65535 65535

exec baseplate-serve2 --bind localhost:9001 $REDDIT_SRC/websockets/example.ini
UPSTART_WEBSOCKETS
fi

service reddit-websockets restart

###############################################################################
# activity service
###############################################################################

if [ ! -f /etc/init/reddit-activity.conf ]; then
    cat > /etc/init/reddit-activity.conf << UPSTART_ACTIVITY
description "activity service"

stop on runlevel [!2345] or reddit-restart all or reddit-restart activity
start on runlevel [2345] or reddit-restart all or reddit-restart activity

respawn
respawn limit 10 5
kill timeout 15

exec baseplate-serve2 --bind localhost:9002 $REDDIT_SRC/activity/example.ini
UPSTART_ACTIVITY
fi

service reddit-activity restart

###############################################################################
# geoip service
###############################################################################
if [ ! -f /etc/gunicorn.d/geoip.conf ]; then
    cat > /etc/gunicorn.d/geoip.conf <<GEOIP
CONFIG = {
    "mode": "wsgi",
    "working_dir": "$REDDIT_SRC/reddit/scripts",
    "user": "$REDDIT_USER",
    "group": "$REDDIT_USER",
    "args": (
        "--bind=127.0.0.1:5000",
        "--workers=1",
         "--limit-request-line=8190",
         "geoip_service:application",
    ),
}
GEOIP
fi

service gunicorn start

###############################################################################
# Job Environment
###############################################################################
CONSUMER_CONFIG_ROOT=$REDDIT_HOME/consumer-count.d

if [ ! -f /etc/default/reddit ]; then
    cat > /etc/default/reddit <<DEFAULT
export REDDIT_ROOT=$REDDIT_SRC/reddit/r2
export REDDIT_INI=$REDDIT_SRC/reddit/r2/run.ini
export REDDIT_USER=$REDDIT_USER
export REDDIT_GROUP=$REDDIT_GROUP
export REDDIT_CONSUMER_CONFIG=$CONSUMER_CONFIG_ROOT
alias wrap-job=$REDDIT_SRC/reddit/scripts/wrap-job
alias manage-consumers=$REDDIT_SRC/reddit/scripts/manage-consumers
DEFAULT
fi

###############################################################################
# Queue Processors
###############################################################################
mkdir -p $CONSUMER_CONFIG_ROOT

function set_consumer_count {
    if [ ! -f $CONSUMER_CONFIG_ROOT/$1 ]; then
        echo $2 > $CONSUMER_CONFIG_ROOT/$1
    fi
}

set_consumer_count search_q 0
set_consumer_count del_account_q 1
set_consumer_count scraper_q 1
set_consumer_count markread_q 1
set_consumer_count commentstree_q 1
set_consumer_count newcomments_q 1
set_consumer_count vote_link_q 1
set_consumer_count vote_comment_q 1
set_consumer_count automoderator_q 0
set_consumer_count butler_q 1
set_consumer_count author_query_q 1
set_consumer_count subreddit_query_q 1
set_consumer_count domain_query_q 1

chown -R $REDDIT_USER:$REDDIT_GROUP $CONSUMER_CONFIG_ROOT/

###############################################################################
# Complete plugin setup, if setup.sh exists
###############################################################################
for plugin in $REDDIT_AVAILABLE_PLUGINS; do
    if [ -x $REDDIT_SRC/$plugin/setup.sh ]; then
        echo "Found setup.sh for $plugin; running setup script"
        $REDDIT_SRC/$plugin/setup.sh $REDDIT_SRC $REDDIT_USER
    fi
done

###############################################################################
# Start everything up
###############################################################################

# the initial database setup should be done by one process rather than a bunch
# vying with eachother to get there first
reddit-run -c 'print "ok done"'

# ok, now start everything else up
initctl emit reddit-stop
initctl emit reddit-start

###############################################################################
# Cron Jobs
###############################################################################
if [ ! -f /etc/cron.d/reddit ]; then
    cat > /etc/cron.d/reddit <<CRON
0    3 * * * root /sbin/start --quiet reddit-job-update_sr_names
30  16 * * * root /sbin/start --quiet reddit-job-update_reddits
0    * * * * root /sbin/start --quiet reddit-job-update_promos
*/5  * * * * root /sbin/start --quiet reddit-job-clean_up_hardcache
*/2  * * * * root /sbin/start --quiet reddit-job-broken_things
*/2  * * * * root /sbin/start --quiet reddit-job-rising
0    * * * * root /sbin/start --quiet reddit-job-trylater

# liveupdate
*    * * * * root /sbin/start --quiet reddit-job-liveupdate_activity

# jobs that recalculate time-limited listings (e.g. top this year)
PGPASSWORD=password
*/15 * * * * $REDDIT_USER $REDDIT_SRC/reddit/scripts/compute_time_listings link year "['hour', 'day', 'week', 'month', 'year']"
*/15 * * * * $REDDIT_USER $REDDIT_SRC/reddit/scripts/compute_time_listings comment year "['hour', 'day', 'week', 'month', 'year']"

# disabled by default, uncomment if you need these jobs
#*    * * * * root /sbin/start --quiet reddit-job-email
#0    0 * * * root /sbin/start --quiet reddit-job-update_gold_users
CRON
fi

###############################################################################
# Finished with install script
###############################################################################
# print this out here. if vagrant's involved, it's gonna do more steps
# afterwards and then re-run this script but that's ok.
$RUNDIR/done.sh


================================================
FILE: install/setup_cassandra.sh
================================================
#!/bin/bash
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

###############################################################################
# Configure Cassandra
###############################################################################

# update the per-thread stack size. this used to be set to 256k in cassandra
# version 1.2.19, but we recently downgraded to 1.2.11 where it's set too low
sed -i -e 's/-Xss180k/-Xss256k/g' /etc/cassandra/cassandra-env.sh

python <<END
import pycassa
sys = pycassa.SystemManager("localhost:9160")

if "reddit" not in sys.list_keyspaces():
    print "creating keyspace 'reddit'"
    sys.create_keyspace("reddit", "SimpleStrategy", {"replication_factor": "1"})
    print "done"

if "permacache" not in sys.get_keyspace_column_families("reddit"):
    print "creating column family 'permacache'"
    sys.create_column_family("reddit", "permacache")
    print "done"
END


================================================
FILE: install/setup_mcrouter.sh
================================================
#!/bin/bash
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

###############################################################################
# Configure mcrouter
###############################################################################
if [ ! -d /etc/mcrouter ]; then
    mkdir -p /etc/mcrouter
fi

if [ ! -f /etc/mcrouter/global.conf ]; then
    cat > /etc/mcrouter/global.conf <<MCROUTER
{
  // route all valid prefixes to the local memcached
  "pools": {
    "local": {
      "servers": [
        "127.0.0.1:11211",
      ],
      "protocol": "ascii",
      "keep_routing_prefix": false,
    },
  },
  "named_handles": [
    {
      "name": "local-pool",
      "type": "PoolRoute",
      "pool": "local",
    },
  ],
  "route": {
    "type": "PrefixSelectorRoute",
    "policies": {
      "rend:": "local-pool",
      "page:": "local-pool",
      "pane:": "local-pool",
      "sr:": "local-pool",
      "account:": "local-pool",
      "link:": "local-pool",
      "comment:": "local-pool",
      "message:": "local-pool",
      "campaign:": "local-pool",
      "award:": "local-pool",
      "trophy:": "local-pool",
      "flair:": "local-pool",
      "friend:": "local-pool",
      "inboxcomment:": "local-pool",
      "inboxmessage:": "local-pool",
      "reportlink:": "local-pool",
      "reportcomment:": "local-pool",
      "reportsr:": "local-pool",
      "reportmessage:": "local-pool",
      "modinbox:": "local-pool",
      "otp:": "local-pool",
      "captcha:": "local-pool",
      "queuedvote:": "local-pool",
      "geoip:": "local-pool",
      "geopromo:": "local-pool",
      "srpromos:": "local-pool",
      "rising:": "local-pool",
      "srid:": "local-pool",
      "defaultsrs:": "local-pool",
      "featuredsrs:": "local-pool",
      "query:": "local-pool",
      "rel:": "local-pool",
      "srmember:": "local-pool",
      "srmemberrel:": "local-pool",
    },
    "wildcard": {
      "type": "PoolRoute",
      "pool": "local",
    },
  },
}
MCROUTER
fi

# this file is sourced by the default mcrouter upstart config, see
# /etc/init/mcrouter.conf
cat > /etc/default/mcrouter <<MCROUTER_DEFAULT
MCROUTER_FLAGS="-f /etc/mcrouter/global.conf -L /var/log/mcrouter/mcrouter.log -p 5050 -R /././ --stats-root=/var/mcrouter/stats"
MCROUTER_DEFAULT

# set an upstart override so mcrouter starts when reddit starts
echo "start on networking or reddit-start" > /etc/init/mcrouter.override

# restart mcrouter to read the updated config
service mcrouter restart


================================================
FILE: install/setup_postgres.sh
================================================
#!/bin/bash
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

# load configuration
RUNDIR=$(dirname $0)
source $RUNDIR/install.cfg

###############################################################################
# Configure PostgreSQL
###############################################################################
SQL="SELECT COUNT(1) FROM pg_catalog.pg_database WHERE datname = 'reddit';"
IS_DATABASE_CREATED=$(sudo -u postgres psql -t -c "$SQL")

if [ $IS_DATABASE_CREATED -ne 1 ]; then
    cat <<PGSCRIPT | sudo -u postgres psql
CREATE DATABASE reddit WITH ENCODING = 'utf8' TEMPLATE template0 LC_COLLATE='en_US.utf8' LC_CTYPE='en_US.utf8';
CREATE USER reddit WITH PASSWORD 'password';
PGSCRIPT
fi

sudo -u postgres psql reddit <<FUNCTIONSQL
create or replace function hot(ups integer, downs integer, date timestamp with time zone) returns numeric as \$\$
    select round(cast(log(greatest(abs(\$1 - \$2), 1)) * sign(\$1 - \$2) + (date_part('epoch', \$3) - 1134028003) / 45000.0 as numeric), 7)
\$\$ language sql immutable;

create or replace function score(ups integer, downs integer) returns integer as \$\$
    select \$1 - \$2
\$\$ language sql immutable;

create or replace function controversy(ups integer, downs integer) returns float as \$\$
    select CASE WHEN \$1 <= 0 or \$2 <= 0 THEN 0
                WHEN \$1 > \$2 THEN power(\$1 + \$2, cast(\$2 as float) / \$1)
                ELSE power(\$1 + \$2, cast(\$1 as float) / \$2)
           END;
\$\$ language sql immutable;

create or replace function ip_network(ip text) returns text as \$\$
    select substring(\$1 from E'[\\d]+\.[\\d]+\.[\\d]+')
\$\$ language sql immutable;

create or replace function base_url(url text) returns text as \$\$
    select substring(\$1 from E'(?i)(?:.+?://)?(?:www[\\d]*\\.)?([^#]*[^#/])/?')
\$\$ language sql immutable;

create or replace function domain(url text) returns text as \$\$
    select substring(\$1 from E'(?i)(?:.+?://)?(?:www[\\d]*\\.)?([^#/]*)/?')
\$\$ language sql immutable;
FUNCTIONSQL


================================================
FILE: install/setup_rabbitmq.sh
================================================
#!/bin/bash
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

# load configuration
RUNDIR=$(dirname $0)
source $RUNDIR/install.cfg

###############################################################################
# Configure RabbitMQ
###############################################################################
if ! sudo rabbitmqctl list_vhosts | egrep "^/$"
then
    sudo rabbitmqctl add_vhost /
fi

if ! sudo rabbitmqctl list_users | egrep "^reddit"
then
    sudo rabbitmqctl add_user reddit reddit
fi

sudo rabbitmqctl set_permissions -p / reddit ".*" ".*" ".*"
sudo rabbitmq-plugins enable rabbitmq_management
sudo service rabbitmq-server restart


================================================
FILE: install/travis.sh
================================================
#!/bin/bash
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

###############################################################################
# reddit travis environment installer
# -----------------------------------
# This script installs a reddit stack suitable for running on travis-ci.
# As such, this is a minimal build to allow for running "nosetests"
# and not much more.
###############################################################################

# load configuration
RUNDIR=$(dirname $0)
source $RUNDIR/install.cfg

# who is running me (expects "travis" or "vagrant")
ENVIRONMENT=${1:-travis}

# the root directory to base the install in. must exist already
REDDIT_CODE=${2:-$REDDIT_SRC/reddit}

if [ ! -e $REDDIT_CODE ]; then
    echo "Couldn't find source $REDDIT_CODE. Aborting"
    exit 1
fi

###############################################################################
# Sanity Checks
###############################################################################
if [[ $EUID -ne 0 ]]; then
    echo "ERROR: Must be run with root privileges."
    exit 1
fi

if [[ "amd64" != $(dpkg --print-architecture) ]]; then
    cat <<END
ERROR: This host is running the $(dpkg --print-architecture) architecture!

Because of the pre-built dependencies in our PPA, and some extra picky things
like ID generation in liveupdate, installing reddit is only supported on amd64
architectures.
END
    exit 1
fi

# seriously! these checks are here for a reason. the packages from the
# reddit ppa aren't built for anything but trusty (14.04) right now, so
# if you try and use this install script on another release you're gonna
# have a bad time.
source /etc/lsb-release
if [ "$DISTRIB_ID" != "Ubuntu" -o "$DISTRIB_RELEASE" != "14.04" ]; then
    echo "ERROR: Only Ubuntu 14.04 is supported."
    exit 1
fi

###############################################################################
# Install prerequisites
###############################################################################
$RUNDIR/install_apt.sh

$RUNDIR/install_cassandra.sh
$RUNDIR/install_zookeeper.sh

###############################################################################
# Install and configure the reddit code
###############################################################################

[ -x "$(which pip)" ] || easy_install pip
pip install -U pip wheel setuptools coverage
pushd $REDDIT_CODE/r2
sudo python setup.py build
python setup.py develop
make
ln -sf example.ini test.ini
popd

###############################################################################
# Install services (for local testing only!)
# NB: this is otherwise handled in the .travis.yml in before_script
###############################################################################
if [ "$ENVIRONMENT" == "vagrant" ]; then
    # install services (cassandra, postgres, etc.)
    $RUNDIR/install_services.sh
    # travis doesn't have mcrouter as a possible service, so we need to
    # be able to test without that running
    service mcrouter stop
    # Configure PostgreSQL
    $RUNDIR/setup_postgres.sh
    # Configure Cassandra
    $RUNDIR/setup_cassandra.sh
    # Configure RabbitMQ
    $RUNDIR/setup_rabbitmq.sh
fi

###############################################################################
# All done!
###############################################################################
cat <<CONCLUSION

Congratulations! A base version of reddit is now installed.  To run the
unit tests:

    cd src/reddit/r2
    nosetests

CONCLUSION


================================================
FILE: install-reddit.sh
================================================
#!/bin/bash
###############################################################################
# reddit dev environment installer
# --------------------------------
# This script installs a reddit stack suitable for development. DO NOT run this
# on a system that you use for other purposes as it might delete important
# files, truncate your databases, and otherwise do mean things to you.
#
# By default, this script will install the reddit code in the current user's
# home directory and all of its dependencies (including libraries and database
# servers) at the system level. The installed reddit will expect to be visited
# on the domain "reddit.local" unless specified otherwise.  Configuring name
# resolution for the domain is expected to be done outside the installed
# environment (e.g. in your host machine's /etc/hosts file) and is not
# something this script handles.
#
# Several configuration options (listed in the "Configuration" section below)
# are overridable with environment variables. e.g.
#
#    sudo REDDIT_DOMAIN=example.com ./install/reddit.sh
#
###############################################################################
set -e

if [[ $EUID -ne 0 ]]; then
    echo "ERROR: Must be run with root privileges."
    exit 1
fi

# load configuration
RUNDIR=$(dirname $0)
SCRIPTDIR="$RUNDIR/install"

# the canonical source of all installers
GITREPO="https://raw.github.com/reddit/reddit/master/install"
NEEDED=(
    "done.sh"
    "install_apt.sh"
    "install_cassandra.sh"
    "install_services.sh"
    "install_zookeeper.sh"
    "reddit.sh"
    "setup_cassandra.sh"
    "setup_mcrouter.sh"
    "setup_postgres.sh"
    "setup_rabbitmq.sh"
    "travis.sh"
)

MISSING=""
for item in ${NEEDED[*]}; do
    if [ ! -x $SCRIPTDIR/$item ]; then
        MISSING="1"
        break
    fi
done

if [ ! -e $SCRIPTDIR/install.cfg ]; then
    NEEDED+=("install.cfg")
    MISSING="1"
fi


function important() {
    echo -e "\033[31m${1}\033[0m"
}

if [ "$MISSING" != "" ]; then
    important "It looks like you're installing without a local repo.  No problem!"
    important "We're going to grab the scripts we need and show you where you can"
    important "edit the config to suit your environment."

    mkdir -p $SCRIPTDIR
    pushd $SCRIPTDIR > /dev/null
    for item in ${NEEDED[*]}; do
        echo "Grabbing '${item}'..."
        wget -q $GITREPO/$item
        chmod +x $item
    done
    popd > /dev/null

    echo "Done!"
fi

echo "#######################################################################"
echo "# Base configuration:"
echo "#######################################################################"
source $SCRIPTDIR/install.cfg
set +x

echo
important "Before proceeding, make sure that these look reasonable.  If not,"
important "you can either edit install/install.cfg or set overrides when running"
important "(they will be respected)."
echo
important "Seriously, if this is your first time installing, stop here and read"
important "the script (install/reddit.sh) and that config. It's got some helpful"
important "information that can prevent common issues."
echo
important "Resolving to the appropriate domain name is beyond the scope of this document,"
important "but the easiest thing is probably editing /etc/hosts on the host machine."
echo
read -er -n1 -p "proceed? [Y/n]" response
if [[ $response =~ ^[Yy]$ || $response == "" ]]; then
    echo "Excellent. Here we go!"
    $SCRIPTDIR/reddit.sh
fi


================================================
FILE: r2/Makefile
================================================
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

SHELL=/bin/sh
PYTHON=python
SED=sed
CAT=cat
BUILD_DIR=build

.PHONY: clean
all: pyx static ini
clean: clean_pyx clean_i18n clean_static

DEFS_PY := Makefile.py
DEFS_FILE := Makefile.defs
.PHONY: $(DEFS_FILE)
$(shell $(PYTHON) $(DEFS_PY) > $(DEFS_FILE))
include $(DEFS_FILE)
ifdef DEFS_SUCCESS
$(info [+] including definitions from $(DEFS_PY))
else
$(error $(DEFS_PY) failed. aborting)
endif

#################### Generated code: Cython + Thrift
PYX_FILES := $(shell find . -name \*.pyx)
PYX_C_FILES := $(PYX_FILES:.pyx=.c)
PYX_SO_FILES := $(PYX_FILES:.pyx=.so)

.PHONY: pyx clean_pyx

pyx:
	$(PYTHON) setup.py build
	$(PYTHON) setup.py build_ext --inplace  # copy the .so files from cython into the source tree

clean_pyx:
	rm -f $(PYX_C_FILES) $(PYX_SO_FILES)

#################### i18n
STRINGS_FILE := r2/lib/generate_strings.py
GENERATED_STRINGS_FILE := r2/lib/generated_strings.py

.PHONY: i18n clean_i18n

# POTFILE is set by Makefile.py
i18n: $(GENERATED_STRINGS_FILE)
	$(PYTHON) setup.py extract_messages --input-dirs=r2,$(PLUGIN_I18N_PATHS) -o $(POTFILE)

$(GENERATED_STRINGS_FILE): $(STRINGS_FILE)
	$(PYTHON) $(STRINGS_FILE) > $(GENERATED_STRINGS_FILE)

clean_i18n:
	rm -f $(GENERATED_STRINGS_FILE)

#################### ini files
UPDATE_FILES := $(wildcard *.update)
INIFILES := $(UPDATE_FILES:.update=.ini)

.PHONY: clean_ini

ini: $(INIFILES)

$(INIFILES): %.ini: %.update example.ini
	  ./updateini.py example.ini $< > $@ || rm $@ 

clean_ini:
	rm $(INIFILES)

#################### CSS file lists
SPRITED_STYLESHEETS += reddit.less compact.css modtools.less expando.less
LESS_STYLESHEETS := wiki.less adminbar.less policies.less reddit-embed.less
OTHER_STYLESHEETS := mobile.css highlight.css

#################### Static Files
STATIC_ROOT := r2/public
STATIC_FILES := $(shell find $(STATIC_ROOT) -readable)
STATIC_BUILD_ROOT := $(BUILD_DIR)/public
STATIC_BUILD_DIR := $(STATIC_BUILD_ROOT)/static
STATIC_BUILDSTAMP := $(BUILD_DIR)/static-buildstamp

.PHONY: clean_static

static: plugin_static pyx css js names

clean_static:
	rm -rf $(STATIC_BUILDSTAMP) $(STATIC_BUILD_DIR) $(PLUGIN_BUILDSTAMPS) $(MANGLE_BUILDSTAMP)

$(STATIC_BUILDSTAMP): $(STATIC_FILES)
	cp -ruTL $(STATIC_ROOT) $(STATIC_BUILD_ROOT)
	touch $@

#################### Plugin static files
PLUGIN_BUILDSTAMPS := $(foreach plugin,$(PLUGINS),$(BUILD_DIR)/plugin-$(plugin)-buildstamp)

plugin_static: $(PLUGIN_BUILDSTAMPS)

define PLUGIN_STATIC_TEMPLATE
$(BUILD_DIR)/plugin-$(1)-buildstamp: $(STATIC_BUILDSTAMP) $(shell find $(PLUGIN_PATH_$(1))/public)
	if [ -f $(PLUGIN_PATH_$(1))/../Makefile ]; then \
		$(MAKE) -C $(PLUGIN_PATH_$(1))/../ static; \
	fi
	cp -r --preserve=timestamps $(PLUGIN_PATH_$(1))/public/* $(STATIC_BUILD_ROOT)/
	touch $$@

$(info [+] adding make rules from plugin "$(1)")
-include $(PLUGIN_PATH_$(1))/../Makefile.plugin
endef
$(foreach plugin,$(PLUGINS),$(eval $(call PLUGIN_STATIC_TEMPLATE,$(plugin))))

#### Stylesheets
LESSC := lessc
CSS_COMPRESS := $(PYTHON) r2/lib/contrib/rcssmin.py
CSS_SOURCE_DIR := $(STATIC_BUILD_DIR)/css

PROCESSED_SPRITED_STYLESHEETS := $(addprefix $(STATIC_BUILD_DIR)/, $(SPRITED_STYLESHEETS:.less=.css))
SPRITES := $(addprefix $(STATIC_BUILD_DIR)/, $(patsubst %.css,sprite-%.png, $(SPRITED_STYLESHEETS:.less=.css)))

LESS_OUTPUTS := $(addprefix $(STATIC_BUILD_DIR)/, $(patsubst %.less,%.css, $(LESS_STYLESHEETS)))

MINIFIED_OTHER_STYLESHEETS := $(addprefix $(STATIC_BUILD_DIR)/, $(OTHER_STYLESHEETS))

PROCESSED_STYLESHEETS := $(PROCESSED_SPRITED_STYLESHEETS) $(MINIFIED_OTHER_STYLESHEETS) $(LESS_OUTPUTS)

CSS_OUTPUTS = $(PROCESSED_STYLESHEETS) $(SPRITES)

.PHONY: clean_css

css: $(STATIC_BUILDSTAMP) $(CSS_OUTPUTS)


# the LESSC invocation is separated so the recipe fails in case of LESS errors.
$(LESS_OUTPUTS): $(STATIC_BUILD_DIR)/%.css : $(CSS_SOURCE_DIR)/%.less
	rm -f $@
	$(LESSC) $< > $@.tmp
	$(CSS_COMPRESS) < $@.tmp > $@
	rm $@.tmp

$(MINIFIED_OTHER_STYLESHEETS): $(STATIC_BUILD_DIR)/%.css: $(CSS_SOURCE_DIR)/%.css
	# when static file names are mangled, the original becomes a symlink to the mangled name
	# remove the original file here in case it's a symlink so we don't just rewrite the old file
	rm -f $@
	$(CAT) $< | $(CSS_COMPRESS) > $@

$(STATIC_BUILD_DIR)/sprite-%.png $(STATIC_BUILD_DIR)/%.css: $(CSS_SOURCE_DIR)/%.less $(STATIC_BUILDSTAMP)
	# see above
	rm -f $(STATIC_BUILD_DIR)/sprite-$*.png $(STATIC_BUILD_DIR)/$*.css
	$(PYTHON) r2/lib/nymph.py $(CSS_SOURCE_DIR)/$*.less $(STATIC_BUILD_DIR)/sprite-$*.png > $(CSS_SOURCE_DIR)/$*.less.tmp
	$(LESSC) $(CSS_SOURCE_DIR)/$*.less.tmp > $(STATIC_BUILD_DIR)/$*.css.tmp
	$(CSS_COMPRESS) < $(STATIC_BUILD_DIR)/$*.css.tmp > $(STATIC_BUILD_DIR)/$*.css
	rm $(CSS_SOURCE_DIR)/$*.less.tmp $(STATIC_BUILD_DIR)/$*.css.tmp

# deprecated; remove once compact.scss has been converted to LESS
$(STATIC_BUILD_DIR)/sprite-%.png $(STATIC_BUILD_DIR)/%.css: $(CSS_SOURCE_DIR)/%.css $(STATIC_BUILDSTAMP)
	# see above
	rm -f $(STATIC_BUILD_DIR)/sprite-$*.png $(STATIC_BUILD_DIR)/$*.css
	$(PYTHON) r2/lib/nymph.py $< $(STATIC_BUILD_DIR)/sprite-$*.png | $(CSS_COMPRESS) > $(STATIC_BUILD_DIR)/$*.css

clean_css:
	rm -f $(CSS_OUTPUTS)

#### JS

.PHONY: clean_js

js: $(STATIC_BUILDSTAMP) $(JS_OUTPUTS)

define JS_MODULE_TEMPLATE
$(JS_MODULE_OUTPUTS_$(1)): $(JS_MODULE_DEPS_$(1)) r2/lib/js.py
	# remove mangled output symlinks, similar to above.
	rm -f $(JS_MODULE_OUTPUTS_$(1))
	$(PYTHON) r2/lib/js.py build_module "$(1)"
	touch $$@
endef

# apply the module template to each of the modules
# so they source their deps from js.py and build accordingly
$(foreach module,$(JS_MODULES),$(eval $(call JS_MODULE_TEMPLATE,$(module))))

clean_js:
	rm -f $(STATIC_BUILD_DIR)/*.js

#### name mangling
MANGLEABLE_FILES := $(CSS_OUTPUTS) $(JS_OUTPUTS)
MANGLE_BUILDSTAMP := $(BUILD_DIR)/mangle-buildstamp
NAMES_FILE := $(STATIC_BUILD_DIR)/names.json
MANGLED_FILES := $(wildcard $(foreach file,$(MANGLEABLE_FILES),$(basename $(file)).*$(suffix $(file))))

.PHONY: clean_names

names: $(MANGLE_BUILDSTAMP)

$(MANGLE_BUILDSTAMP): $(MANGLEABLE_FILES)
	$(PYTHON) r2/lib/static.py $(NAMES_FILE) $(MANGLEABLE_FILES)
	touch $@

clean_names:
	rm -f $(MANGLE_BUILDSTAMP) $(NAMES_FILE) $(MANGLEABLE_FILES) $(MANGLED_FILES)

$(shell rm -f $(DEFS_FILE))


================================================
FILE: r2/Makefile.py
================================================
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################
import os

from r2.lib.translation import I18N_PATH
from r2.lib.plugin import PluginLoader
from r2.lib import js

print 'POTFILE := ' + os.path.join(I18N_PATH, 'r2.pot')

plugins = PluginLoader()
print 'PLUGINS := ' + ' '.join(plugin.name for plugin in plugins
                               if plugin.needs_static_build)

print 'PLUGIN_I18N_PATHS := ' + ','.join(os.path.relpath(plugin.path)
                                         for plugin in plugins
                                         if plugin.needs_translation)

import sys
for plugin in plugins:
    print 'PLUGIN_PATH_%s := %s' % (plugin.name, plugin.path)

js.load_plugin_modules(plugins)
modules = dict((k, m) for k, m in js.module.iteritems())
print 'JS_MODULES := ' + ' '.join(modules.iterkeys())
outputs = []
for name, module in modules.iteritems():
    outputs.extend(module.outputs)
    print 'JS_MODULE_OUTPUTS_%s := %s' % (name, ' '.join(module.outputs))
    print 'JS_MODULE_DEPS_%s := %s' % (name, ' '.join(module.dependencies))

print 'JS_OUTPUTS := ' + ' '.join(outputs)
print 'DEFS_SUCCESS := 1'


================================================
FILE: r2/babel.cfg
================================================
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

# .html files in JS are underscore templates, not mako
[javascript:**/public/static/js/**.html]

# Extraction from Mako templates

[mako:**.html]
input_encoding = utf-8
[mako:**.email]
input_encoding = utf-8
[mako:**.xml]
input_encoding = utf-8
[mako:**.htmllite]
input_encoding = utf-8

# Extraction from Python source files

[python:**.py]

[javascript:**.js]
encoding = utf-8


================================================
FILE: r2/check-code
================================================
#!/usr/bin/python
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################
"""Check for new style guide violations in the current branch.

This script is meant to be used in a CI process to ensure that new changes
do not violate PEP-8, PEP-257, or any of the validity checks of pyflakes.

"""
import argparse
import collections
import difflib
import logging
import lxml.etree as etree
import os
import re
import subprocess
import sys


DEVNULL = open("/dev/null", "w")
TOOLS = collections.OrderedDict((
    ('pep8', ["pep8", "--repeat"]),
    ('pep257', ["pep257"]),
    ('pyflakes', ["pyflakes"]),
))


# Match *.py and *.pyx
PYFILE = re.compile(r".*\.pyx?$")


def assert_tools_available():
    """Check if the external binaries needed are available or exit."""
    for tool in TOOLS.values():
        binary = tool[0]
        try:
            subprocess.check_call(["which", binary], stdout=DEVNULL)
        except subprocess.CalledProcessError:
            logging.error("command %r not found. please install it!", binary)
            sys.exit(1)


def assert_not_dirty():
    """Check if there are uncommitted changes in the repo and exit if so."""
    try:
        subprocess.check_call(["git", "diff",
                               "--no-ext-diff", "--quiet", "--exit-code"])
    except subprocess.CalledProcessError:
        logging.error("you have uncommitted changes. please commit them!")
        sys.exit(1)


def _parse_ref(ref):
    """Return the result of git rev-parse on the given ref."""
    ref = subprocess.check_output(["git", "rev-parse", ref])
    return ref.strip()


def get_current_ref():
    """Return the most descriptive name possible of the current HEAD."""
    try:
        ref = subprocess.check_output(["git", "symbolic-ref", "HEAD"]).strip()
        return ref[len("refs/heads/"):]
    except subprocess.CalledProcessError:
        return _parse_ref("HEAD")


def get_upstream_ref():
    """Return the ref that this topic branch is based on."""
    return _parse_ref("master@{upstream}")


def get_merge_base():
    upstream = get_upstream_ref()
    current = get_current_ref()
    output = subprocess.check_output(['git', 'merge-base', upstream, current])
    return output.strip()


def get_root():
    """Return the root directory of this git project."""
    return os.path.dirname(_parse_ref("--git-dir"))


def check_ref_out(ref):
    """Ask git to check out the specified ref."""
    try:
        subprocess.check_call(
            ["git", "checkout", ref],
            stdout=DEVNULL,
            stderr=DEVNULL,
        )
    except subprocess.CalledProcessError:
        logging.error("failed to check out %s", ref)
        sys.exit(1)


def walk_workspace():
    root = get_root()
    files = subprocess.check_output(['git', 'ls-files', '--full-name',
                                     '--', root])
    for filename in files.splitlines():
        yield os.path.join(root, filename)


def select_files(files):
    for f in files:
        if re.match(PYFILE, f):
            yield f
        else:
            try:
                with open(f) as f_:
                    first = f_.readline()
                    if first.startswith('#!') and 'python' in first:
                        yield f
            except (IOError, OSError):
                logging.exception("Unable to check-code against %s", f)


def extract_errtype(violation, tool):
    """Based on a line of `tool`'s output, return the kind of infraction.

    Mostly relevant for pep8, which has various kinds of infractions,
    such as E501 for line too long.

    """
    if tool == 'pep8':
        # E501 line too long (91 characters)
        errtype, sep, message = violation.partition(" ")
        if not sep:
            errtype = 'PEP8'
    elif tool == 'pep257':
        errtype = 'PEP257'
    elif tool == 'pyflakes':
        errtype = 'pyflakes'
    return errtype


def make_test_class(tool, filepath):
    no_ext, ext = os.path.splitext(filepath)
    test_class_suffix = no_ext.replace(os.path.sep, '_')
    return tool + '.' + test_class_suffix


def extract_line_info(reportline, filepath, tool):
    if tool == 'pep257' and reportline.startswith('Note: checks'):
        return None
    file_info, sep, violation = reportline.partition(": ")
    if not sep:
        return None
    file_info = file_info.split(":")
    if len(file_info) < 2:
        logging.warn("I don't understand this report line: %r", reportline)
        line_num = ''
    else:
        line_num = file_info[1]
    report_entry = {
        'file': filepath,
        'test_class': make_test_class(tool, filepath),
        'line': line_num,
        'violation': violation,
        'errtype': extract_errtype(violation, tool),
        'tool': tool,
    }
    return report_entry


def generate_report(toolname, files=None):
    if not files:
        files = walk_workspace()

    report = []
    for filepath in select_files(files):
        command = TOOLS[toolname] + [filepath]
        logging.info(" ".join(command))
        process = subprocess.Popen(
            command,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
        )

        lines = process.communicate()[0].splitlines()
        ws_root = get_root()
        ws_filepath = os.path.relpath(filepath, ws_root)
        for line in lines:
            line = extract_line_info(line, ws_filepath, toolname)
            if line:
                report.append(line)
    return report


def generate_all_reports(ref=None, files=None):
    """Run the tools on the specified files and return errors / warnings."""
    if ref:
        check_ref_out(ref)

    report = collections.OrderedDict.fromkeys(TOOLS.keys())
    for tool in TOOLS:
        report[tool] = generate_report(tool, files)

    return report


def get_changed_files(old_ref, new_ref):
    """Return a list of files that have changed from one ref to another."""
    root = get_root()
    changed_files_text = subprocess.check_output(["git", "diff", "--name-only",
                                                  old_ref, new_ref])
    changed_files = changed_files_text.splitlines()
    return [os.path.join(root, x) for x in changed_files]


def diffable(report):
    """Convert the report to a list of lines that are reasonably 'diffable'.

    That is, standard diff tools should be able to identify new or fixed
    violations by comparing results of this function

    """
    updated = []
    for toolname, violations in report.iteritems():
        updated.append(toolname)
        updated.extend('%(file)s %(violation)s' % v for v in violations)
        updated.append('')
    return updated


def human(report):
    """Convert the report to a list of human useful lines."""
    updated = []
    for toolname, violations in report.iteritems():
        updated.append(toolname)
        updated.extend('%(file)s:%(line)s %(violation)s' % v
                       for v in violations)
        updated.append('')
    return updated


def junitize(report):
    """Convert the report into JUnit style XML.

    This allows the report to be consumed by tools that consume JUnit reports

    The style used here is: each file is a <testsuite>; each violation
    will be a <testcase> (always failed) whose "classname" shall be the tool
    used (e.g., pep8) and "name" shall be the type of violation and line
    number. Any additional information shall be included as the <failure>
    message.

    """
    by_file = {}
    for violations in report.itervalues():
        for violation in violations:
            file_errors = by_file.setdefault(violation['file'], [])
            file_errors.append(violation)
    violations = etree.Element("testsuites")
    for filename in by_file:
        file_errs = etree.SubElement(violations, "testsuite")
        for violation in by_file[filename]:
            entry = etree.SubElement(file_errs, "testcase")
            entry.attrib['classname'] = violation['test_class']
            entry.attrib['name'] = violation['line']
            error_info = etree.SubElement(entry, "failure")
            error_info.attrib['message'] = violation['violation']
            error_info.attrib['type'] = violation['errtype']
    return violations


def make_errname(violation):
    """Create a unique "test name" for this violation."""
    name = '.'.join((violation['errtype'], violation['line']))
    return name


def diff_report(options):
    if options.check_dirty:
        assert_not_dirty()

    current_ref = get_current_ref()
    base_ref = get_merge_base()
    if options.files:
        files = options.files
    else:
        files = get_changed_files(base_ref, current_ref)
        logging.debug("files changed: %r", files)

    try:
        new_report = diffable(generate_all_reports(current_ref, files))
        logging.debug("new report:\n%r", new_report)
        old_report = diffable(generate_all_reports(base_ref, files))
        logging.debug("old report:\n%r", old_report)
    finally:
        check_ref_out(current_ref)

    return difflib.unified_diff(old_report, new_report)


def regression_report(options):
    added, removed = 0, 0
    for line in diff_report(options):
        line = line.strip()
        if line == "+++" or line == "---":
            continue
        if line.startswith("+"):
            added += 1
        elif line.startswith("-"):
            removed += 1

    if added:
        print >> options.out, "added %d issues" % added
    if removed:
        print >> options.out, "removed %d issues!" % removed

    return 1 if added else 0


def junit_report(options):
    report = generate_all_reports(files=options.files)
    junit = junitize(report)
    print >> options.out, etree.tostring(junit, pretty_print=True)


def human_report(options):
    if options.full:
        report = human(generate_all_reports(files=options.files))
    else:
        files = (options.files or
                 get_changed_files(get_merge_base(), get_current_ref()))
        logging.debug("changed files: %r", files)
        report = human(generate_all_reports(files=files))
    for line in report:
        print >> options.out, line


def parse_args(args):
    parser = argparse.ArgumentParser(description="Report on python problems")
    parser.add_argument('--dirty', dest='check_dirty', action='store_false',
                        help="Skip the dirty workspace check.")
    parser.add_argument('-O', dest='out', type=argparse.FileType('w'),
                        default=sys.stdout, help="Write the report to OUT"
                        " instead of stdout.")
    parser.add_argument('--verbose', '-v', action='count', dest='verbosity',
                        help="Show verbose reporting messages.",
                        default=0)
    parser.add_argument('--quiet', '-q', action='count', default=0,
                        help="Reduce verbosity")
    parser.add_argument('--full', action='store_true', help="When generating"
                        " a {report}, show all files, not just changed ones.")
    parser.add_argument('report', choices=('junit', 'regression', 'report'))
    parser.add_argument('files', nargs='*', metavar='FILE')
    options = parser.parse_args(args)
    set_up_logging(options.verbosity - options.quiet)
    logging.debug("Options: %r", options)
    return options


def set_up_logging(verbosity):
    levels = {-2: logging.ERROR, -1: logging.WARN, 0: logging.INFO,
              1: logging.DEBUG}
    max_level = max(levels.keys())
    min_level = min(levels.keys())
    verbosity = min(verbosity, max_level)
    verbosity = max(verbosity, min_level)
    level = levels[verbosity]
    format_ = '%(levelname)s %(message)s'
    logging.basicConfig(level=level, format=format_)


def main():
    options = parse_args(sys.argv[1:])
    if options.report == 'regression':
        command = regression_report
    elif options.report == 'junit':
        command = junit_report
    elif options.report == 'report':
        command = human_report
    assert_tools_available()
    sys.exit(command(options))

if __name__ == "__main__":
    main()


================================================
FILE: r2/coverage.sh
================================================
#!/bin/bash
set -e

BASEDIR=$(readlink -f $(dirname $0))
cd $BASEDIR

VERSION=$(git rev-parse HEAD)
COVERDIR="$BASEDIR/build/cover-$VERSION"

function usage() {
    echo "Run unit tests and coverage reports on reddit codebase with optional"
    echo "http server to the report"
    echo
    echo "Usage: `basename $0` [options]";
    echo
    echo "  -h           show this message"
    echo "  -p \$PORT     run an simple http server on \$PORT to view results"
    echo "  -v           verbose mode (set -x)"
    echo
}

while getopts ":vhp:" opt; do
  case $opt in
    p) PORT="$OPTARG" ;;
    v) set -x ;;
    h)
      usage
      exit 0
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      usage
      exit 1
      ;;
    :)
      echo "Option -$OPTARG requires an argument." >&2
      exit 1
      ;;
  esac
done

nosetests \
    --with-coverage \
    --cover-html \
    --cover-html-dir=$COVERDIR \
    --cover-erase \
    --cover-package=r2

if [ "$PORT" != "" ]; then
    echo "Starting http server on :$PORT (^C to exit)"
    pushd $COVERDIR
    python -m SimpleHTTPServer $PORT || echo "Done."
    popd
fi
rm -r $COVERDIR


================================================
FILE: r2/pylintrc
================================================
[MASTER]

# Specify a configuration file.
#rcfile=

# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=

# Profiled execution.
profile=no

# Add <file or directory> to the black list. It should be a base name, not a
# path. You may set this option multiple times.
ignore=CVS

# Pickle collected data for later comparisons.
persistent=yes

# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=


[MESSAGES CONTROL]

# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time.
#enable=

# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once).
# E1103: X has no Y member (but some types could not be inferred)
# W0212: Access to a protected member of X class
# W0223: Method Y is abstract in class X but not overridden
# C0103: Invalid name "%s" (should match %s)
# C0111: Missing docstring
# W0142: Used * or ** magic
# R0201: Method could be a function
# R0915: Too many statements
# I0011: Locally disabling ... (don't need to see things we've explicitly disabled)
disable=E1103,W0212,W0223,C0111,W0142,R0201,R0915,I0011,C0103


[REPORTS]

# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html
output-format=text

# Include message's id in output
include-ids=yes

# Put messages in a separate file for each module / package specified on the
# command line instead of printing them on stdout. Reports (if any) will be
# written in a file name "pylint_global.[txt|html]".
files-output=no

# Tells whether to display a full report or only the messages
reports=no

# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)

# Add a comment according to your evaluation note. This is used by the global
# evaluation report (RP0004).
comment=no


[BASIC]

# Required attributes for module, separated by a comma
required-attributes=

# List of builtins function names that should not be used, separated by a comma
bad-functions=apply,input

# Regular expression which should only match correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$

# Regular expression which should only match correct module level names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$

# Regular expression which should only match correct class names
class-rgx=[A-Z_][a-zA-Z0-9]+$

# Regular expression which should only match correct function names
function-rgx=[a-z_][a-z0-9_]{2,30}$

# Regular expression which should only match correct method names
method-rgx=[a-z_][a-z0-9_]{2,30}$

# Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-z0-9_]{2,30}$

# Regular expression which should only match correct argument names
argument-rgx=[a-z_][a-z0-9_]{2,30}$

# Regular expression which should only match correct variable names
variable-rgx=[a-z_][a-z0-9_]{2,30}$

# Regular expression which should only match correct list comprehension /
# generator expression variable names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$

# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,c,g,ex,Run,_

# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata

# Regular expression which should only match functions or classes name which do
# not require a docstring
no-docstring-rgx=__.*__


[FORMAT]

# Maximum number of characters on a single line.
max-line-length=80

# Maximum number of lines in a module
max-module-lines=1000

# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string='    '


[MISCELLANEOUS]

# List of note tags to take in consideration, separated by a comma.
notes=FIXME,XXX,TODO


[SIMILARITIES]

# Minimum lines number of a similarity.
min-similarity-lines=4

# Ignore comments when computing similarities.
ignore-comments=yes

# Ignore docstrings when computing similarities.
ignore-docstrings=yes


[TYPECHECK]

# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes

# List of classes names for which member attributes should not be checked
# (useful for classes with attributes dynamically set).
ignored-classes=SQLObject

# When zope mode is activated, add a predefined set of Zope acquired attributes
# to generated-members.
zope=no

# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E0201 when accessed.
generated-members=REQUEST,acl_users,aq_parent


[VARIABLES]

# Tells whether we should check for unused import in __init__ files.
init-import=no

# A regular expression matching the beginning of the name of dummy variables
# (i.e. not used).
dummy-variables-rgx=unused|dummy

# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=


[CLASSES]

# List of interface methods to ignore, separated by a comma. This is used for
# instance to not check methods defines in Zope's Interface base class.
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by

# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp


[DESIGN]

# Maximum number of arguments for function / method
max-args=5

# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*

# Maximum number of locals for function / method body
max-locals=15

# Maximum number of return / yield for function / method body
max-returns=6

# Maximum number of branch for function / method body
max-branchs=25

# Maximum number of statements in function / method body
max-statements=50

# Maximum number of parents for a class (see R0901).
max-parents=7

# Maximum number of attributes for a class (see R0902).
max-attributes=7

# Minimum number of public methods for a class (see R0903).
min-public-methods=0

# Maximum number of public methods for a class (see R0904).
max-public-methods=20


[IMPORTS]

# Deprecated modules which should not be used, separated by a comma
deprecated-modules=regsub,string,TERMIOS,Bastion,rexec

# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=

# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=

# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=


================================================
FILE: r2/r2/__init__.py
================================================
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

"""r2

This file loads the finished app from r2.config.middleware.
"""

# _strptime is imported with PyImport_ImportModuleNoBlock which can fail
# miserably when multiple threads try to import it simultaneously.
# import this here to get it over with
# see "Non Blocking Module Imports" in:
# http://code.google.com/p/modwsgi/wiki/ApplicationIssues
import _strptime

# defer the (hefty) import until it's actually needed. this allows
# modules below r2 to be imported before cython files are built, also
# provides a hefty speed boost to said imports when they don't need
# the app initialization.
def make_app(*args, **kwargs):
    from r2.config.middleware import make_app as real_make_app
    return real_make_app(*args, **kwargs)


================================================
FILE: r2/r2/commands.py
================================================
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

import os, sys

import paste.fixture
from paste.script import command
from paste.deploy import loadapp

from r2.lib.log import RavenErrorReporter


class RunCommand(command.Command):
    max_args = 2
    min_args = 1

    usage = "CONFIGFILE CMDFILE.py"
    summary = "Executed CMDFILE with pylons support"
    group_name = "Reddit"


    parser = command.Command.standard_parser(verbose=True)
    parser.add_option('-c', '--command',
                      dest='command',
                      help="execute command in module")
    parser.add_option("", "--proctitle",
                      dest="proctitle",
                      help="set the title seen by ps and top")

    def command(self):
        try:
            if self.options.proctitle:
                import setproctitle
                setproctitle.setproctitle("paster " + self.options.proctitle)
        except ImportError:
            pass

        config_file = self.args[0]
        config_name = 'config:%s' % config_file

        report_to_sentry = "REDDIT_ERRORS_TO_SENTRY" in os.environ

        here_dir = os.getcwd()

        # Load locals and populate with objects for use in shell
        sys.path.insert(0, here_dir)

        # Load the wsgi app first so that everything is initialized right
        global_conf = {
            'running_as_script': "true",
        }
        wsgiapp = loadapp(
            config_name, relative_to=here_dir, global_conf=global_conf)
        test_app = paste.fixture.TestApp(wsgiapp)

        # Query the test app to setup the environment
        tresponse = test_app.get('/_test_vars')
        request_id = int(tresponse.body)

        # Disable restoration during test_app requests
        test_app.pre_request_hook = lambda self: \
            paste.registry.restorer.restoration_end()
        test_app.post_request_hook = lambda self: \
            paste.registry.restorer.restoration_begin(request_id)

        # Restore the state of the Pylons special objects
        # (StackedObjectProxies)
        paste.registry.restorer.restoration_begin(request_id)

        loaded_namespace = {}

        try:
            if self.args[1:]:
                execfile(self.args[1], loaded_namespace)

            if self.options.command:
                exec self.options.command in loaded_namespace
        except Exception:
            if report_to_sentry:
                exc_info = sys.exc_info()
                RavenErrorReporter.capture_exception(exc_info=exc_info)
            raise


================================================
FILE: r2/r2/config/__init__.py
================================================
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################



================================================
FILE: r2/r2/config/environment.py
================================================
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

import os
import mimetypes

from mako.lookup import TemplateLookup
from pylons.error import handle_mako_error
from pylons.configuration import PylonsConfig

import r2.lib.helpers
from r2.config.paths import (
    get_r2_path,
    get_built_statics_path,
    get_raw_statics_path,
)
from r2.config.routing import make_map
from r2.lib.app_globals import Globals
from r2.lib.configparse import ConfigValue


mimetypes.init()


def load_environment(global_conf={}, app_conf={}, setup_globals=True):
    r2_path = get_r2_path()
    root_path = os.path.join(r2_path, 'r2')

    paths = {
        'root': root_path,
        'controllers': os.path.join(root_path, 'controllers'),
        'templates': [os.path.join(root_path, 'templates')],
    }

    if ConfigValue.bool(global_conf.get('uncompressedJS')):
        paths['static_files'] = get_raw_statics_path()
    else:
        paths['static_files'] = get_built_statics_path()

    config = PylonsConfig()

    config.init_app(global_conf, app_conf, package='r2', paths=paths)

    # don't put action arguments onto c automatically
    config['pylons.c_attach_args'] = False

    # when accessing non-existent attributes on c, return "" instead of dying
    config['pylons.strict_tmpl_context'] = False

    g = Globals(config, global_conf, app_conf, paths)
    config['pylons.app_globals'] = g

    if setup_globals:
        config['r2.import_private'] = \
            ConfigValue.bool(global_conf['import_private'])
        g.setup()
        g.plugins.declare_queues(g.queues)

    g.plugins.load_plugins(config)
    config['r2.plugins'] = g.plugins
    g.startup_timer.intermediate("plugins")

    config['pylons.h'] = r2.lib.helpers
    config['routes.map'] = make_map(config)

    #override the default response options
    config['pylons.response_options']['headers'] = {}

    # when mako loads a previously compiled template file from its cache, it
    # doesn't check that the original template path matches the current path.
    # in the event that a new plugin defines a template overriding a reddit
    # template, unless the mtime newer, mako doesn't update the compiled
    # template. as a workaround, this makes mako store compiled templates with
    # the original path in the filename, forcing it to update with the path.
    if "cache_dir" in app_conf:
        module_directory = os.path.join(app_conf['cache_dir'], 'templates')

        def mako_module_path(filename, uri):
            filename = filename.lstrip('/').replace('/', '-')
            path = os.path.join(module_directory, filename + ".py")
            return os.path.abspath(path)
    else:
        # disable caching templates since we don't know where they should go.
        module_directory = mako_module_path = None

    # set up the templating system
    config["pylons.app_globals"].mako_lookup = TemplateLookup(
        directories=paths["templates"],
        error_handler=handle_mako_error,
        module_directory=module_directory,
        input_encoding="utf-8",
        default_filters=["conditional_websafe"],
        filesystem_checks=getattr(g, "reload_templates", False),
        imports=[
            "from r2.lib.filters import websafe, unsafe, conditional_websafe",
            "from pylons import request",
            "from pylons import tmpl_context as c",
            "from pylons import app_globals as g",
            "from pylons.i18n import _, ungettext",
        ],
        modulename_callable=mako_module_path,
    )

    if setup_globals:
        g.setup_complete()

    return config


================================================
FILE: r2/r2/config/extensions.py
================================================
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

from pylons import tmpl_context as c

def api_type(subtype = ''):
    return 'api-' + subtype if subtype else 'api'

def is_api(subtype = ''):
    return c.render_style and c.render_style.startswith(api_type(subtype))

def get_api_subtype():
    if is_api() and c.render_style.startswith('api-'):
        return c.render_style[4:]

extension_mapping = {
    "rss": ("xml", "application/atom+xml; charset=UTF-8"),
    "xml": ("xml", "application/atom+xml; charset=UTF-8"),
    "js": ("js", "text/javascript; charset=UTF-8"),
    "embed": ("htmllite", "text/javascript; charset=UTF-8"),
    "mobile": ("mobile", "text/html; charset=UTF-8"),
    "png": ("png", "image/png"),
    "css": ("css", "text/css"),
    "csv": ("csv", "text/csv; charset=UTF-8"),
    "api": (api_type(), "application/json; charset=UTF-8"),
    "json-html": (api_type("html"), "application/json; charset=UTF-8"),
    "json-compact": (api_type("compact"), "application/json; charset=UTF-8"),
    "compact": ("compact", "text/html; charset=UTF-8"),
    "json": (api_type(), "application/json; charset=UTF-8"),
    "i": ("compact", "text/html; charset=UTF-8"),
}

API_TYPES = ('api', 'json')
RSS_TYPES = ('rss', 'xml')

def set_extension(environ, ext):
    environ["extension"] = ext
    environ["render_style"], environ["content_type"] = extension_mapping[ext]


================================================
FILE: r2/r2/config/feature/README.md
================================================
# Feature

`r2.config.feature` is reddit's feature flagging API. It lets us quickly
switch on and off features for specific segments of users and requests.

It's inspired by Etsy's feature framework, at
https://github.com/etsy/feature - if you're looking to add to this, you may
want to check there first to see if there's learning to be had. There almost
certainly is.

## Use

Using the feature API is simple. At its core:

```python

from r2.config import feature

if feature.is_enabled('some_flag'):
    result = do_new_thing()
else:
    result = do_old_thing()
```

Or in a mako template:

```html

% if feature.is_enabled('some_flag'):
  <strong>New thing!</strong>
% else:
  <span>Old thing.</span>
% endif
```


Along with a component in live_config, currently as an "on" or "off" symbol or JSON:

```ini

# Completely On
feature_some_flag = on

# Completely Off
feature_some_flag = off

# On for admin
feature_some_flag = {"admin": true}

# On for employees
feature_some_flag = {"employee": true}

# On for gold users
feature_some_flag = {"gold": true}

# On for users with the beta preference enabled
feature_some_flag = {"beta": true}

# On for logged in users
feature_some_flag = {"loggedin": true}

# On for logged out users
feature_some_flag = {"loggedout": true}

# On by URL, like ?feature=public_flag_name
feature_some_flag = {"url": "public_flag_name"}

# On by group of users
feature_some_flag = {"users": ["umbrae", "ajacksified"]}

# On when viewing certain subreddits
feature_some_flag = {"subreddits": ["wtf", "aww"]}

# On by subdomain
feature_some_flag = {"subdomains": ["beta"]}

# On by OAuth client IDs
feature_some_flag = {"oauth_clients": ["xyzABC123"]}

# On for a percentage of loggedin users (0 being no users, 100 being all of them)
feature_some_flag = {"percent_loggedin": 25}

# On for a percentage of loggedout users (0 being no users, 100 being all of them)
# N.B: This is based on the value of the `loid` cookie, if there is no `loid`
# cookie the feature will be off.
# The `loid` cookie is currently set in JavaScript, so you can't expect it to
# exist on the first visit or in requests made by API clients.
feature_some_flag = {"percent_loggedout": 25}

# For both admin and a group of users
feature_some_flag = {"admin": true, "users": ["user1", "user2"]}
```

Since we're currently overloading live_config, each feature flag should be
prepended with `feature_` in the config. We may choose to make a live-updating
features block in the future.

You can also use feature flags to define A/B-type experiments.  Logically,
experiments are separated into two parts.  First, there is an *eligibility
check* to determine if the user is allowed to be a part of the experiment;
eligibility is determined by the same selectors as above with the exception of
`percent_loggedin` and `percent_loggedout` which would be redundant.  
Secondly, eligible users are either *bucketed* into a variant or *excluded*
(because the summed percentage of all variants is less than 100).  `is_enabled`
will return False for users who are non-eligible, fall into a control group, or
are excluded; for anyone for whom this is true, you should call `variant` to
find the specific variant they fall into.

In code, this looks something like this:

```python
from r2.config import feature

if feature.is_enabled('some_flag'):
    variant = feature.variant('some_flag')
    if variant == 'test_something':
        do_new_thing()
    elif variant == 'test_something_else':
        do_other_new_thing()
    else:
        raise NotImplementedError('unknown variant %s for some_flag' % variant)
else:
    do_old_thing()
```

with a live_config option defining the experiment parameters:

```ini
# loggedin only experiment with two test variants
feature_some_flag = {"experiment": {"loggedin": true, "experiment_id": 12345, "variants": {"test_something": 5.5, "test_something_else": 10}}}

# Or with custom control group sizes:
feature_some_flag = {"experiment": {"loggedin": true, "experiment_id": 12345, "variants": {"test_something": 5.5, "test_something_else": 10, "control_1": 20, "control_2": 20}}}

# these can be mixed and matched with other selectors (and will OR)
# this will enable the flag for gold users, and then run an experiment for other logged in users
feature_some_flag = {"gold": true, "experiment": {"loggedin": true, "experiment_id": 12345, "variants": {"test_something": 5.5, "test_something_else": 10, "control_1": 20, "control_2": 20}}}
```

If only one non-control variant is defined (an A/A/B test), the code can be
simplified a little bit:

```python
from r2.config import feature

if feature.is_enabled('some_flag'):
    do_new_thing()
else:
    do_old_thing()
```

The experiment dict has a few fields:

* **experiment_id** -- an integer.  While the feature name needs to be unique
  across all currently-defined feature flags, the experiment id should be
  unique across all time.  This allows the data team to uniquely identify
  experiments while looking at historical data.
* **variants** -- a dictionary mapping variant names to percentages.  The
  percent indicates roughly how many eligible users will be chosen to be a part
  of that variant.  Percentages should not exceed 100/n, where n is the number
  of variants.  The number of variants should not change over the course of the
  experiment, but the percentages allocated each can.  Percentages can be
  specified to the tenths of percentages.  If not defined, two control
  groups ("control_1" and "control_2") at 10% each will be automatically added
  to the variants.
* **enabled** -- a boolean, defaulting to true.  Set to false to temporarily
  disable an experiment while still keeping its definition around.

Since it's useful to be able to force bucketing for testing purposes, you can
specify a variant with a secondary syntax for a few flag conditions:

```ini
# ?feature=some_flag_something will force the "test_something" variant and
# ?feature=some_flag_something_else will force "test_something_else"
feature_some_flag = {"url": {"some_flag_something": "test_something", "some_flag_something_else": "test_something_else"}}
```

## When should I use this?

This is useful for a whole lot of reasons.

* To admin-launch something to the company for review before it goes live to
  everyone, and staging isn't a good fit.

* To release something to third party devs and mods before it goes live

* To gradually add traffic to something that may have serious
  impact on load

* To guard something that you might need to quickly turn off for some reason
  or another. Load shedding, security, etc.


## Style guidelines

Copied essentially wholesale from Etsy's guidelines:

To make it easier to push features through the life cycle there are a
few coding guidelines to observe.

First, the feature name argument to the Feature method (`is_enabled`) should
always be a string literal. This will make it easier to find all the places
that a particular feature is checked. If you find yourself creating feature
names at run time and then checking them, you’re probably abusing the Feature
system. Chances are in such a case you don’t really want to be using the
Feature API but rather simply driving your code with some plain old config
data.

Second, the results of the Feature methods should not be cached, such
as by calling `feature.is_enabled` once and storing the result in an
instance variable of some controller. The Feature machinery already
caches the results of the computation it does so it should already be
plenty fast to simply call `feature.is_enabled` whenever needed. This
will again aid in finding the places that depend on a particular feature.

Third, as a check that you’re using the Feature API properly, whenever
you have an if block whose test is a call to `feature.is_enabled`,
make sure that it would make sense to either remove the check and keep
the code or to delete the check and the code together. There shouldn’t
be bits of code within a block guarded by an is_enabled check that
needs to be salvaged if the feature is removed.


================================================
FILE: r2/r2/config/feature/__init__.py
================================================
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

from r2.config.feature.feature import is_enabled, variant, all_enabled


================================================
FILE: r2/r2/config/feature/feature.py
================================================
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

from r2.config.feature.state import FeatureState
from r2.config.feature.world import World
from r2.lib.hooks import HookRegistrar

feature_hooks = HookRegistrar()

_world = World()
_featurestate_cache = {}


def is_enabled(name, user=None, subreddit=None):
    """Test and return whether a given feature is enabled for this request.

    If `feature` is not found, returns False.

    The optional arguments allow overriding that you generally don't want, but
    is useful outside of request contexts - cron jobs and the like.

    :param name string - a given feature name
    :param user - (optional) an Account
    :param subreddit - (optional) a Subreddit
    :return bool
    """
    if not user:
        user = _world.current_user()
    if not subreddit:
        subreddit = _world.current_subreddit()
    subdomain = _world.current_subdomain()
    oauth_client = _world.current_oauth_client()

    return _get_featurestate(name).is_enabled(
        user=user,
        subreddit=subreddit,
        subdomain=subdomain,
        oauth_client=oauth_client,
    )

def variant(name, user=None):
    """Return which variant of an experiment a user is part of.

    If the experiment is not found, has no variants, or the user is not part of
    any of them (control), return None.

    :param name string - an experiment (feature) name
    :param user - (optional) an Account.  Defaults to the currently signed in
                  user.
    :return string, or None if not part of an experiment
    """
    if not user:
        user = _world.current_user()

    return _get_featurestate(name).variant(user)

def all_enabled(user=None):
    """Return a list of enabled features and experiments for the user.
    
    Provides the user's assigned variant and the experiment ID for experiments.

    This does not trigger bucketing events, so it should not be used for
    feature flagging purposes on the server. It is meant to let clients
    condition features on experiment variants. Those clients should manually
    send the appropriate bucketing events.

    This does not include page-based experiments, which operate independently
    of the particular user.

    :param user - (optional) an Account. Defaults to None, for which we
                  determine logged-out features.
    :return dict - a dictionary mapping enabled feature keys to True or to the
                   experiment/variant information
    """
    features = FeatureState.get_all(_world)

    # Get enabled features and experiments
    active = {}
    for feature in features:
        experiment = feature.config.get('experiment')
        # Exclude page experiments
        if experiment and FeatureState.is_user_experiment(experiment):
            # Get experiment names, ids, and assigned variants, leaving out
            # experiments for which this user is excluded
            variant = feature.variant(user)
            if variant:
                active[feature.name] = {
                    'experiment_id': experiment.get('experiment_id'),
                    'variant': variant
                }
        elif feature.is_enabled(user):
                active[feature.name] = True

    return active

@feature_hooks.on('worker.live_config.update')
def clear_featurestate_cache():
    global _featurestate_cache
    _featurestate_cache = {}


def _get_featurestate(name):
    """Get a FeatureState object for this feature, creating it if necessary.

    :param name string - a given feature name
    :return FeatureState
    """

    featurestate = _featurestate_cache.get(name, None)
    if featurestate is None:
        featurestate = FeatureState(name, _world)
        _featurestate_cache[name] = featurestate

    return featurestate


================================================
FILE: r2/r2/config/feature/state.py
================================================
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

import logging
import json
import hashlib

from pylons import tmpl_context as c
from pylons import app_globals as g


class FeatureState(object):
    """A FeatureState is the state of a feature and its condition in the world.

    It determines if this feature is enabled given the world provided.
    """

    # Special values for globally enabled properties - no need to interrogate
    # the world for these values.
    GLOBALLY_ON = "on"
    GLOBALLY_OFF = "off"

    # constant config blocks
    DISABLED_CFG = {"enabled": GLOBALLY_OFF}
    ENABLED_CFG = {"enabled": GLOBALLY_ON}

    # The number of buckets to use for any bucketing operations.  Should always
    # be evenly divisible by 100.  Each factor of 10 over 100 gives us an
    # additional digit of precision.
    NUM_BUCKETS = 1000

    # The variant definition for control groups that are added by default.
    DEFAULT_CONTROL_GROUPS = {'control_1': 10, 'control_2': 10}

    def __init__(self, name, world, config_name=None, config_str=None):
        self.name = name
        self.world = world
        self.config = self._parse_config(name, config_name, config_str)

    def _parse_config(self, name, config_name=None, config_str=None):
        """Find and parse a config from our live config with this given name.

        :param name string - a given feature name
        :return dict - a dictionary with at least "enabled". May include more
                       depending on the enabled type.
        """
        if not config_name:
            config_name = "feature_%s" % name

        if not config_str:
            config_str = self.world.live_config(config_name)

        if not config_str or config_str == FeatureState.GLOBALLY_OFF:
            return self.DISABLED_CFG

        if config_str == FeatureState.GLOBALLY_ON:
            return self.ENABLED_CFG

        try:
            config = json.loads(config_str)
        except (ValueError, TypeError) as e:
            g.log.warning("Could not load config for name %r - %r",
                          config_name, e)
            return self.DISABLED_CFG

        if not isinstance(config, dict):
            g.log.warning("Config not dict, on or off: %r", config_name)
            return self.DISABLED_CFG

        return config

    @staticmethod
    def get_all(world):
        """Return FeatureState objects for all features in live_config.

        Creates a FeatureState object for every config entry prefixed with
        "feature_".

        :param world - World proxy object to the app/request state.
        """
        features = []
        for (key, config_str) in world.live_config_iteritems():
            if key.startswith('feature_'):
                feature_state = FeatureState(key[8:], world, key, config_str)
                features.append(feature_state)
        return features

    @staticmethod
    def is_user_experiment(experiment):
        return not FeatureState.is_page_experiment(experiment)

    @staticmethod
    def is_page_experiment(experiment):
        return experiment.get('page')

    def _calculate_bucket(self, seed, experiment_seed=None):
        """Sort something into one of self.NUM_BUCKETS buckets.

        :param seed -- a string used for shifting the deterministic bucketing
                       algorithm.  In most cases, this will be an Account's
                       _fullname.
        :return int -- a bucket, 0 <= bucket < self.NUM_BUCKETS
        """
        # Mix the feature name in with the seed so the same users don't get
        # selected for ramp-ups for every feature.
        hashed = hashlib.sha1(self.name + seed)
        bucket = long(hashed.hexdigest(), 16) % self.NUM_BUCKETS
        return bucket

    @classmethod
    def _choose_variant(cls, bucket, variants):
        """Deterministically choose a percentage-based variant.

        The algorithm satisfies two conditions:

        1. It's deterministic (that is, every call with the same bucket and
           variants will result in the same answer).
        2. An increase in any of the variant percentages will keep the same
           buckets in the same variants as at the smaller percentage (that is,
           all buckets previously put in variant A will still be in variant A,
           all buckets previously put in variant B will still be in variant B,
           etc. and the increased percentages will be made of up buckets
           previously not assigned to a bucket).

        These attributes make it suitable for use in A/B experiments that may
        see an increase in their variant percentages post-enabling.

        :param bucket -- an integer bucket representation
        :param variants -- a dictionary of
                           <string:variant name>:<float:percentage> pairs.  If
                           any percentage exceeds 1/n percent, where n is the
                           number of variants, the percentage will be capped to
                           1/n.  These variants will be added to
                           DEFAULT_CONTROL_GROUPS to create the effective
                           variant set.
        :return string -- the variant name, or None if bucket doesn't fall into
                          any of the variants
        """
        # We want to always include two control groups, but allow overriding of
        # their percentages.
        all_variants = dict(cls.DEFAULT_CONTROL_GROUPS)
        all_variants.update(variants)

        # Say we have an experiment with two new things we're trying out for 2%
        # of users (A and B), a control group with 5% (C), and a pool of
        # excluded users (x).  The buckets will be assigned like so:
        #
        #     A B C A B C x x C x x C x x C x x x x x x x x x...
        #
        # This scheme allows us to later increase the size of A and B to 7%
        # while keeping the experience consistent for users in any group other
        # than excluded users:
        #
        #     A B C A B C A B C A B C A B C A B x A B x x x x...
        #
        # Rather than building this entire structure out in memory, we can use
        # a little bit of math to figure out just the one bucket's value.
        num_variants = len(all_variants)
        variant_names = sorted(all_variants.keys())
        # If the variants took up the entire set of buckets, which bucket would
        # we be in?
        candidate_variant = variant_names[bucket % num_variants]
        # Log a warning if this variant is capped, to help us prevent user (us)
        # error.  It's not the most correct to only check the one, but it's
        # easy and quick, and anything with that high a percentage should be
        # selected quite often.
        variant_fraction = all_variants[candidate_variant] / 100.0
        variant_cap = 1.0 / num_variants
        if variant_fraction > variant_cap:
            g.log.warning(
                'Variant %s exceeds allowable percentage (%.2f > %.2f)',
                candidate_variant,
                variant_fraction,
                variant_cap,
            )
        # Variant percentages are expressed as numeric percentages rather than
        # a fraction of 1 (that is, 1.5 means 1.5%, not 150%); thus, at 100
        # buckets, buckets and percents map 1:1 with each other.  Since we may
        # have more than 100 buckets (causing each bucket to represent less
        # than 1% each), we need to scale up how far "right" we move for each
        # variant percent.
        bucket_multiplier = cls.NUM_BUCKETS / 100
        # Now check to see if we're far enough left to be included in the
        # variant percentage.
        if bucket < (all_variants[candidate_variant] * num_variants *
                     bucket_multiplier):
            return candidate_variant
        else:
            return None

    @classmethod
    def _is_variant_enabled(cls, variant):
        """Determine if a variant is "enabled", as returned by is_enabled."""
        # The excluded experimental group will have a `None` variant and
        # this feature should be disabled.
        # For users in control groups, the feature is considered "not
        # enabled" because they should get the same behavior as ineligible
        # users.
        return (
            variant is not None and
            variant not in cls.DEFAULT_CONTROL_GROUPS
        )

    def is_enabled(self, user=None, subreddit=None, subdomain=None,
                   oauth_client=None):
        """Determine if a feature is enabled.

        For experiments, this induces a bucketing event by calling
        self._is_experiment_enabled.
        """
        cfg = self.config
        kw = dict(
            user=user,
            subreddit=subreddit,
            subdomain=subdomain,
            oauth_client=oauth_client
        )
        # first, test if the config would be enabled without an experiment
        if self._is_config_enabled(cfg, **kw):
            return True

        # next, test if the config is enabled fractionally
        if self._is_percent_enabled(cfg, user=user):
            return True

        # lastly, check experiment
        experiment = self.config.get('experiment')
        if self._is_config_enabled(experiment, **kw):
            return self._is_experiment_enabled(experiment, user=user)

        # Unknown value, default to off.
        return False

    def _is_config_enabled(
        self, cfg, user=None, subreddit=None, subdomain=None,
        oauth_client=None
    ):
        world = self.world

        if not cfg:
            return False

        if cfg.get('enabled') == self.GLOBALLY_ON:
            return True

        if cfg.get('enabled') == self.GLOBALLY_OFF:
            return False

        url_flag = cfg.get('url')
        if url_flag:
            if isinstance(url_flag, dict):
                for feature in world.url_features():
                    if feature in url_flag:
                        return self._is_variant_enabled(url_flag[feature])
            elif url_flag in world.url_features():
                return True

        if cfg.get('admin') and world.is_admin(user):
            return True

        if cfg.get('employee') and world.is_employee(user):
            return True

        if cfg.get('beta') and world.user_has_beta_enabled(user):
            return True

        if cfg.get('gold') and world.has_gold(user):
            return True

        loggedin = world.is_user_loggedin(user)
        if cfg.get('loggedin') and loggedin:
            return True

        if cfg.get('loggedout') and not loggedin:
            return True

        users = [u.lower() for u in cfg.get('users', [])]
        if users and user and user.name.lower() in users:
            return True

        subreddits = [s.lower() for s in cfg.get('subreddits', [])]
        if subreddits and subreddit and subreddit.lower() in subreddits:
            return True

        subdomains = [s.lower() for s in cfg.get('subdomains', [])]
        if subdomains and subdomain and subdomain.lower() in subdomains:
            return True

        clients = set(cfg.get('oauth_clients', []))
        if clients and oauth_client and oauth_client in clients:
            return True

    def _is_percent_enabled(self, cfg, user=None):
        loggedin = self.world.is_user_loggedin(user)
        percent_loggedin = cfg.get('percent_loggedin', 0)
        if percent_loggedin and loggedin:
            bucket = self._calculate_bucket(user._fullname)
            scaled_percent = bucket / (self.NUM_BUCKETS / 100)
            if scaled_percent < percent_loggedin:
                return True

        percent_loggedout = cfg.get('percent_loggedout', 0)
        if percent_loggedout and not loggedin:
            # We want this to match the JS function for bucketing loggedout
            # users, and JS doesn't make it easy to mix the feature name in
            # with the LOID. Just look at the last 4 chars of the LOID.
            loid = self.world.current_loid()
            if loid:
                try:
                    bucket = int(loid[-4:], 36) % 100
                    if bucket < percent_loggedout:
                        return True
                except ValueError:
                    pass

    def _is_experiment_enabled(self, experiment, user=None):
        """ Determine if there's an active variant of the specified experiment
        for the current user.

        Sends a bucketing event.
        """
        if not experiment.get('enabled', True):
            return False

        variant = None
        if FeatureState.is_user_experiment(experiment):
            variant = self._get_user_experiment_variant(experiment, user)
        elif FeatureState.is_page_experiment(experiment):
            content_id, _ = FeatureState.get_content_id()
            variant = self._get_page_experiment_variant(experiment)

        # We only want to send this event once per request, because that's
        # an easy way to get rid of extraneous events.
        if not c.have_sent_bucketing_event:
            c.have_sent_bucketing_event = set()

        if variant is not None and self.world.valid_experiment_request():
            if FeatureState.is_user_experiment(experiment):
                loid = self.world.current_loid()
                if self.world.is_user_loggedin(user):
                    bucketing_id = user._id
                else:
                    bucketing_id = loid

                key = ('user', self.name, bucketing_id)

                if (
                    g.running_as_script or
                    key not in c.have_sent_bucketing_event
                ):
                    g.events.bucketing_event(
                        experiment_id=experiment.get('experiment_id'),
                        experiment_name=self.name,
                        variant=variant,
                        user=user,
                        loid=self.world.current_loid_obj(),
                    )
                    c.have_sent_bucketing_event.add(key)
            else:
                # This is a page experiment, so we know we have a content_id
                key = ('page', self.name, content_id)
                if (
                    g.running_as_script or
                    key not in c.have_sent_bucketing_event
                ):
                    g.events.page_bucketing_event(
                        experiment_id=experiment.get('experiment_id'),
                        experiment_name=self.name,
                        variant=variant,
                        content_id=content_id,
                        request=request,
                        context=c,
                    )
                    c.have_sent_bucketing_event.add(key)

        return self._is_variant_enabled(variant)

    def variant(self, user):
        """ Determine which variant of this experiment, if any, is active.

        Does not send a bucketing event.
        """
        url_flag = self.config.get('url')
        # We only care about the dict-type 'url_flag's, since those are the
        # only ones that can specify a variant.
        if url_flag and isinstance(url_flag, dict):
            for feature in self.world.url_features():
                try:
                    return url_flag[feature]
                except KeyError:
                    pass

        experiment = self.config.get('experiment')
        if not experiment:
            return None

        if FeatureState.is_user_experiment(experiment):
            return self._get_user_experiment_variant(experiment, user)
        return self._get_page_experiment_variant(experiment)

    def _get_user_experiment_variant(self, experiment, user):
        # for logged in users, bucket based on the User's fullname
        if self.world.is_user_loggedin(user):
            bucket = self._calculate_bucket(user._fullname)
        # for logged out users, bucket based on the loid if we have one
        elif g.enable_loggedout_experiments:
            loid = self.world.current_loid()
            # we can't run an experiment if we have no id to vary on.
            if not loid:
                return None
            bucket = self._calculate_bucket(loid)
        # if logged out experiments are disabled, bail.
        else:
            return None

        variant = self._choose_variant(bucket, experiment.get('variants', {}))
        return variant

    @staticmethod
    def get_content_id():
        from r2.lib import utils
        thing = utils.url_to_thing(request.fullurl)

        if not thing:
            return None, None

        content_id = None
        type_name = getattr(thing, '_type_name', None)
        if type_name == 'comment':
            # We use the parent link for comment permalink pages, since
            # they share a canonical URL
            link = getattr(thing, 'link', thing.link_slow)
            content_id = link._fullname
        elif type_name == 'link':
            content_id = thing._fullname
        elif type_name == 'subreddit':
            content_id = thing._fullname
        return content_id, type_name

    def _get_page_experiment_variant(self, experiment):
        content_id, type_name = FeatureState.get_content_id()

        if content_id is None:
            return None

        # If we've restricted the experiment to certain page types, make sure
        # the request is for one of those
        if (experiment.get('subreddit_only', False) and
                type_name != 'subreddit'):
            return None

        if (experiment.get('link_only', False) and
                (type_name != 'link' and type_name != 'comment')):
            # We treat comment permalink pages like general comments pages
            return None

        experiment_seed = experiment.get('experiment_seed', None)
        bucket = self._calculate_bucket(content_id, experiment_seed)
        variant = self._choose_variant(bucket, experiment.get('variants', {}))
        return variant


================================================
FILE: r2/r2/config/feature/world.py
================================================
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

from pylons import request
from pylons import tmpl_context as c
from pylons import app_globals as g


class World(object):
    """A World is the proxy to the app/request state for Features.

    Proxying through World allows for easy testing and caching if needed.
    """

    @staticmethod
    def stacked_proxy_safe_get(stacked_proxy, key, default=None):
        """Get a field from a StackedObjectProxy

        Always succeeds, even if the proxy has not yet been initialized.
        Normally, if the proxy hasn't been initialized, a `TypeError` is
        raised to indicate a programming error. To avoid crashing on feature
        checks that are done too early (e.g., during initial DB set-up of
        the pylons environment), this function will instead return `default`
        for an uninitialized proxy.

        (Initialized proxies ALWAYS return a value, either a set value
        or an empty string)

        """
        try:
            return getattr(stacked_proxy, key)
        except TypeError:
            return default

    def current_user(self):
        if c.user_is_loggedin:
            return self.stacked_proxy_safe_get(c, 'user')

    def current_subreddit(self):
        site = self.stacked_proxy_safe_get(c, 'site')
        if not site:
            # In non-request code (eg queued jobs), there isn't necessarily a
            # site name (or other request-type data).  In those cases, we don't
            # want to trigger any subreddit-specific code.
            return ''
        return site.name

    def current_subdomain(self):
        return self.stacked_proxy_safe_get(c, 'subdomain')

    def current_oauth_client(self):
        client = self.stacked_proxy_safe_get(c, 'oauth2_client', None)
        return getattr(client, '_id', None)

    def current_loid_obj(self):
        return self.stacked_proxy_safe_get(c, 'loid')

    def current_loid(self):
        loid = self.current_loid_obj()
        if not loid:
            return None
        return loid.loid

    def is_admin(self, user):
        if not user or not hasattr(user, 'name'):
            return False

        return user.name in self.stacked_proxy_safe_get(g, 'admins', [])

    def is_employee(self, user):
        if not user:
            return False
        return user.employee

    def user_has_beta_enabled(self, user):
        if not user:
            return False
        return user.pref_beta

    def has_gold(self, user):
        if not user:
            return False

        return user.gold

    def is_user_loggedin(self, user):
        if not (user or self.current_user()):
            return False
        return True

    def url_features(self):
        return set(request.GET.getall('feature'))

    def live_config(self, name):
        live = self.stacked_proxy_safe_get(g, 'live_config', {})
        return live.get(name)

    def live_config_iteritems(self):
        live = self.stacked_proxy_safe_get(g, 'live_config', {})
        return live.iteritems()

    def simple_event(self, name):
        stats = self.stacked_proxy_safe_get(g, 'stats', None)
        if stats:
            return stats.simple_event(name)


================================================
FILE: r2/r2/config/hooks.py
================================================
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################


def register_hooks():
    """Register all known non-plugin hooks. Called on app setup."""
    from r2.config.feature.feature import feature_hooks
    feature_hooks.register_all()

    from r2.models.admintools import admintools_hooks
    admintools_hooks.register_all()

    from r2.models.account import trylater_hooks
    trylater_hooks.register_all()

    from r2.models import subreddit
    subreddit.trylater_hooks.register_all()


================================================
FILE: r2/r2/config/middleware.py
================================================
# The contents of this file are subject to the Common Public Attribution
# License Version 1.0. (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
# software over a computer network and provide for limited attribution for the
# Original Developer. In addition, Exhibit A has been modified to be consistent
# with Exhibit B.
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
#
# The Original Code is reddit.
#
# The Original Developer is the Initial Developer.  The Initial Developer of
# the Original Code is reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2015 reddit
# Inc. All Rights Reserved.
###############################################################################

"""Pylons middleware initialization"""
import importlib
import re
import urllib
import tempfile
import urlparse
from threading import Lock
import itertools
import simplejson

from paste.cascade import Cascade
from paste.errordocument import StatusBasedForward
from paste.recursive import RecursiveMiddleware
from paste.registry import RegistryManager
from paste.urlparser import StaticURLParser
from paste.deploy.converters import asbool
from paste.request import path_info_split
from pylons import response
from pylons.middleware import ErrorHandler
from pylons.wsgiapp import PylonsApp
from routes.middleware import RoutesMiddleware

from r2.config import hooks
from r2.config.environment import load_environment
from r2.config.extensions import extension_mapping, set_extension
from r2.lib.utils import is_subdomain, is_language_subdomain
from r2.lib import csrf, filters


# patch in WebOb support for HTTP 429 "Too Many Requests"
import webob.exc
import webob.util

class HTTPTooManyRequests(webob.exc.HTTPClientError):
    code = 429
    title = 'Too Many Requests'
    explanation = ('The server has received too many requests from the client.')

webob.exc.status_map[429] = HTTPTooManyRequests
webob.util.status_reasons[429] = HTTPTooManyRequests.title

# patch out SSRFable/XSSable endpoints in older versions of weberror
import weberror.evalexception


# We could probably just set `.exposed = False`, but this makes me feel better
def _stub(*args, **kwargs):
    pass

weberror.evalexception.EvalException.post_traceback = _stub
weberror.evalexception.EvalException.relay = _stub


def error_mapper(code, message, environ, global_conf=None, **kw):
    if environ.get('pylons.error_call'):
        return None
    else:
        environ['pylons.error_call'] = True

    from pylons import tmpl_context as c

    if global_conf is None:
        global_conf = {}
    codes = [304, 400, 401, 403, 404, 409, 415, 429, 503]
    if not asbool(global_conf.get('debug')):
        codes.append(500)
    if code in codes:
        # StatusBasedForward expects a relative URL (no SCRIPT_NAME)
        d = dict(code = code, message = message)

        exception = environ.get('r2.controller.exception')
        if exception:
            d['explanation'] = exception.explanation
            error_data = getattr(exception, 'error_data', None)
            if error_data:
                environ['extra_error_data'] = error_data

        if environ.get('REDDIT_NAME'):
            d['srname'] = environ.get('REDDIT_NAME')
        if environ.get('REDDIT_TAKEDOWN'):
            d['takedown'] = environ.get('REDDIT_TAKEDOWN')
        if environ.get('REDDIT_ERROR_NAME'):
            d['error_name'] = environ.get('REDDIT_ERROR_NAME')

        # preserve x-frame-options when 304ing
        if code == 304:
            d['allow_framing'] = 1 if c.allow_framing else 0

        extension = environ.get("extension")
        if extension:
            url = '/error/document/.%s?%s' % (extension, urllib.urlencode(d))
        else:
            url = '/error/document/?%s' % (urllib.urlencode(d))
        return url


# from pylons < 1.0
def ErrorDocuments(app, global_conf, mapper, **kw):
    """Wraps the app in error docs using Paste RecursiveMiddleware and
    ErrorDocumentsMiddleware
    """
    if global_conf is None:
        global_conf = {}

    return RecursiveMiddleware(StatusBasedForward(
        app, global_conf=global_conf, mapper=mapper, **kw))


class ProfilingMiddleware(object):
    def __init__(self, app, directory):
        self.app = app
        self.directory = directory

    def __call__(self, environ, start_response):
        import cProfile

        try:
            tmpfile = tempfile.NamedTemporaryFile(prefix='profile',
                                                  dir=self.directory,
                                                  delete=False)

            profile = cProfile.Profile()
            result = profile.runcall(self.app, environ, start_response)
            profile.dump_stats(tmpfile.name)

            return result
        finally:
            tmpfile.close()


class DomainMiddleware(object):

    def __init__(self, app, config):
        self.app = app
        self.config = config

    def __call__(self, environ, start_response):
        g = self.config['pylons.app_globals']
        http_host = environ.get('HTTP_HOST', 'localhost').lower()
        domain, s, port = http_host.partition(':')

        # remember the port
        try:
            environ['request_port'] = int(port)
        except ValueError:
            pass

        # localhost is exempt so paster run/shell will work
        # media_domain doesn't need special processing since it's just ads
        is_media_only_domain = (is_subdomain(domain, g.media_domain) and
                                g.domain != g.media_domain)
        if domain == "localhost" or is_media_only_domain:
            return self.app(environ, start_response)

        # tell reddit_base to redirect to the appropriate subreddit for
        # a legacy CNAME
        if not is_subdomain(domain, g.domain):
            environ['legacy-cname'] = domain
            return self.app(environ, start_response)

        # How many characters to chop off the end of the hostname before
        # we start looking at subdomains
        ignored_suffix_len = len(g.domain)

        # figure out what subdomain we're on, if any
        subdomains = domain[:-ignored_suffix_len - 1].split('.')

        sr_redirect = None
        prefix_parts = []
        for subdomain in subdomains[:]:
            extension = g.extension_subdomains.get(subdomain)
            # These subdomains are reserved, don't treat them as SR
            # or language subdomains.
            if subdomain in g.reserved_subdomains:
                # Some subdomains are reserved, but also can't be mixed into
                # the domain prefix for various reasons (permalinks will be
                # broken, etc.)
                if subdomain in g.ignored_subdomains:
                    continue
                prefix_parts.append(subdomain)
            elif extension:
                environ['reddit-domain-extension'] = extension
            elif is_language_subdomain(subdomain):
                environ['reddit-prefer-lang'] = subdomain
            else:
                sr_redirect = subdomain
                subdomains.remove(subdomain)

        if 'reddit-prefer-lang' in environ:
            prefix_parts.insert(0, environ['reddit-prefer-lang'])
        if prefix_parts:
            environ['reddit-domain-prefix'] = '.'.join(prefix_parts)

        # if there was a subreddit subdomain, redirect
        if sr_redirect and environ.get("FULLPATH"):
            if not subdomains and g.domain_prefix:
                subdomains.append(g.domain_prefix)
            subdomains.append(g.domain)
            redir = "%s/r/%s/%s" % ('.'.join(subdomains),
                                    sr_redirect, environ['FULLPATH'])
            redir = g.default_scheme + "://" + redir.replace('//', '/')

            start_response("301 Moved Permanently", [("Location", redir)])
            return [""]

        return self.app(environ, start_response)


class SubredditMiddleware(object):
    sr_pattern = re.compile(r'^/r/([^/]{2,})')

    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        path = environ['PATH_INFO']
        sr = self.sr_pattern.match(path)
        if sr:
            environ['subreddit'] = sr.groups()[0]
            environ['PATH_INFO'] = self.sr_pattern.sub('', path) or '/'
        return self.app(environ, start_response)


class DomainListingMiddleware(object):
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        if not environ.has_key('subreddit'):
            path = environ['PATH_INFO']
            domain, rest = path_info_split(path)
            if domain == "domain" and rest:
                domain, rest = path_info_split(rest)
                environ['domain'] = domain
                environ['PATH_INFO'] = rest or '/'
        return self.app(environ, start_response)


class ExtensionMiddleware(object):
    ext_pattern = re.compile(r'\.([^/]+)\Z')

    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        path = environ['PATH_INFO']
        fname, sep, path_ext = path.rpartition('.')
        domain_ext = environ.get('reddit-domain-extension')

        ext = None
        if path_ext in extension_mapping:
            ext = path_ext
            # Strip off the extension.
            environ['PATH_INFO'] = path[:-(len(ext) + 1)]
        elif domain_ext in extension_mapping:
            ext = domain_ext

        if ext:
            set_extension(environ, ext)
        else:
            environ['render_style'] = 'html'
            environ['content_type'] = 'text/html; charset=UTF-8'

        return self.app(environ, start_response)

class FullPathMiddleware(object):
    # Debt: we have a lot of middleware which (unfortunately) modify the
    # global URL PATH_INFO string. To work with the original request URL, we
    # save it to a different location here.
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        environ['FULLPATH'] = environ.get('PATH_INFO')
        qs = environ.get('QUERY_STRING')
        if qs:
            environ['FULLPATH'] += '?' + qs
        return self.app(environ, start_response)

class StaticTestMiddleware(object):
    def __init__(self, app, static_path, domain):
        self.app = app
        self.static_path = static_path
        self.domain = domain

    def __call__(self, environ, start_response):
        if environ['HTTP_HOST'] == self.domain:
            environ['PATH_INFO'] = self.static_path.rstrip('/') + environ['PATH_INFO']
            return self.app(environ, start_response)
        raise webob.exc.HTTPNotFound()


def _wsgi_json(start_response, status_int, message=""):
    status_message = webob.util.status_reasons[status_int]
    message = message or status_message

    start_response(
        "%s %s" % (status_int, status_message),
        [("Content-Type", "application/json")])

    data = simplejson.dumps({
        "error": status_int,
        "message": message
    })
    return [filters.websafe_json(data).encode("utf-8")]


class LimitUploadSize(object):
    """
    Middleware for restricting the size of uploaded files (such as
    image files for the CSS editing capability).
    """
    def __init__(self, app, max_size=1024*500):
        self.app = app
        self.max_size = max_size

    def __call__(self, environ, start_response):
        cl_key = 'CONTENT_LENGTH'
        is_error = environ.get("pylons.error_call", False)
        is_api = environ.get("render_style").startswith("api")
        if not is_error and environ['REQUEST_METHOD'] == 'POST':
            if cl_key not in environ:

                if is_api:
                    return _wsgi_json(start_response, 411)
                else:
                    start_response("411 Length Required", [])
                    return ['<html><body>length required</body></html>']

            try:
                cl_int = int(environ[cl_key])
            except ValueError:
                if is_api:
                    return _wsgi_json(start_response, 400)
                else:
                    start_response("400 Bad Request", [])
                    return ['<html><body>bad request</body></html>']

            if cl_int > self.max_size:
                error_msg = "too big. keep it under %d KiB" % (
                    self.max_size / 1024)

                if is_api:
                    return _wsgi_json(start_response, 413, error_msg)
                else:
                    start_response("413 Too Big", [])
                    return ["<html>"
                            "<head>"
                            "<script type='text/javascript'>"
                            "parent.completedUploadImage('failed',"
                            "'',"
                            "'',"
                            "[['BAD_CSS_NAME', ''], ['IMAGE_ERROR', '", error_msg,"']],"
                            "'');"
                            "</script></head><body>you shouldn\'t be here</body></html>"]

        return self.app(environ, start_response)

# TODO CleanupMiddleware seems to exist because cookie headers are being duplicated
# somewhere in the response processing chain. It should be removed as soon as we
# find the underlying issue.
class CleanupMiddleware(object):
    """
    Put anything here that should be called after every other bit of
    middleware. This currently includes the code for removing
    duplicate headers (such as multiple cookie setting).  The behavior
    here is to disregard all but the last record.
    """
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        def custom_start_response(status, headers, exc_info = None):
            fixed = []
            seen = set()
            for head, val in reversed(headers):
                head = head.lower()
                key = (head, val.split("=", 1)[0])
                if key not in seen:
                    fixed.insert(0, (head, val))
                    seen.add(key)
            return start_response(status, fixed, exc_info)
        return self.app(environ, custom_start_response)


class SafetyMiddleware(object):
    """Clean up any attempts at response splitting in headers."""

    has_bad_characters = re.compile("[\r\n]")
    sanitizer = re.compile("[\r\n]+[ \t]*")

    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        def safe_start_response(status, headers, exc_info=None):
            sanitized = []
            for name, value in headers:
                if self.has_bad_characters.search(value):
                    value = self.sanitizer.sub("", value)
                sanitized.append((name, value))
            return start_response(status, sanitized, exc_info)
        return self.app(environ, safe_start_response)


class RedditApp(PylonsApp):

    test_mode = False

    def __init__(self, *args, **kwargs):
        super(RedditApp, self).__init__(*args, **kwargs)
        self._loading_lock = Lock()
        self._controllers = None
        self._hooks_registered = False

    def setup_app_env(self, environ, start_response):
        PylonsApp.setup_app_env(self, environ, start_response)

        if not self.test_mode:
            if self._controllers and self._hooks_registered:
                return

            with self._loading_lock:
                self.load_controllers()
                self.register_hooks()

    def _check_csrf_prevention(self):
        from r2 import controllers
        from pylons import 
Download .txt
gitextract_4c_ynblg/

├── .drone.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── SECURITY.md
├── Vagrantfile
├── install/
│   ├── README.md
│   ├── done.sh
│   ├── drone.sh
│   ├── install.cfg
│   ├── install_apt.sh
│   ├── install_cassandra.sh
│   ├── install_services.sh
│   ├── install_zookeeper.sh
│   ├── reddit.sh
│   ├── setup_cassandra.sh
│   ├── setup_mcrouter.sh
│   ├── setup_postgres.sh
│   ├── setup_rabbitmq.sh
│   └── travis.sh
├── install-reddit.sh
├── r2/
│   ├── Makefile
│   ├── Makefile.py
│   ├── babel.cfg
│   ├── check-code
│   ├── coverage.sh
│   ├── pylintrc
│   ├── r2/
│   │   ├── __init__.py
│   │   ├── commands.py
│   │   ├── config/
│   │   │   ├── __init__.py
│   │   │   ├── environment.py
│   │   │   ├── extensions.py
│   │   │   ├── feature/
│   │   │   │   ├── README.md
│   │   │   │   ├── __init__.py
│   │   │   │   ├── feature.py
│   │   │   │   ├── state.py
│   │   │   │   └── world.py
│   │   │   ├── hooks.py
│   │   │   ├── middleware.py
│   │   │   ├── paths.py
│   │   │   ├── queues.py
│   │   │   ├── routing.py
│   │   │   └── templates.py
│   │   ├── controllers/
│   │   │   ├── __init__.py
│   │   │   ├── admin.py
│   │   │   ├── api.py
│   │   │   ├── api_docs.py
│   │   │   ├── apiv1/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── gold.py
│   │   │   │   ├── login.py
│   │   │   │   ├── scopes.py
│   │   │   │   └── user.py
│   │   │   ├── awards.py
│   │   │   ├── buttons.py
│   │   │   ├── captcha.py
│   │   │   ├── embed.py
│   │   │   ├── error.py
│   │   │   ├── front.py
│   │   │   ├── googletagmanager.py
│   │   │   ├── health.py
│   │   │   ├── ipn.py
│   │   │   ├── listingcontroller.py
│   │   │   ├── login.py
│   │   │   ├── mailgun.py
│   │   │   ├── mediaembed.py
│   │   │   ├── multi.py
│   │   │   ├── newsletter.py
│   │   │   ├── oauth2.py
│   │   │   ├── oembed.py
│   │   │   ├── policies.py
│   │   │   ├── post.py
│   │   │   ├── promotecontroller.py
│   │   │   ├── reddit_base.py
│   │   │   ├── redirect.py
│   │   │   ├── robots.py
│   │   │   ├── toolbar.py
│   │   │   ├── web.py
│   │   │   └── wiki.py
│   │   ├── data/
│   │   │   └── locations.json
│   │   ├── lib/
│   │   │   ├── __init__.py
│   │   │   ├── amqp.py
│   │   │   ├── app_globals.py
│   │   │   ├── authorize/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── api.py
│   │   │   │   └── interaction.py
│   │   │   ├── automoderator.py
│   │   │   ├── base.py
│   │   │   ├── baseplate_integration.py
│   │   │   ├── butler.py
│   │   │   ├── c/
│   │   │   │   └── filters.c
│   │   │   ├── cache.py
│   │   │   ├── cache_poisoning.py
│   │   │   ├── captcha.py
│   │   │   ├── comment_tree.py
│   │   │   ├── configparse.py
│   │   │   ├── contrib/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── activity.thrift
│   │   │   │   ├── dtds/
│   │   │   │   │   ├── README
│   │   │   │   │   └── allowed_entities.dtd
│   │   │   │   ├── ipaddress.py
│   │   │   │   ├── rcssmin.py
│   │   │   │   └── simpleflake.py
│   │   │   ├── cookies.py
│   │   │   ├── count.py
│   │   │   ├── csrf.py
│   │   │   ├── cssfilter.py
│   │   │   ├── db/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── _sorts.pyx
│   │   │   │   ├── alter_db.py
│   │   │   │   ├── operators.py
│   │   │   │   ├── queries.py
│   │   │   │   ├── sorts.py
│   │   │   │   ├── tdb_cassandra.py
│   │   │   │   ├── tdb_lite.py
│   │   │   │   ├── tdb_sql.py
│   │   │   │   ├── thing.py
│   │   │   │   └── userrel.py
│   │   │   ├── einhorn.py
│   │   │   ├── emailer.py
│   │   │   ├── embeds.py
│   │   │   ├── emr_helpers.py
│   │   │   ├── errors.py
│   │   │   ├── eventcollector.py
│   │   │   ├── export.py
│   │   │   ├── filters.py
│   │   │   ├── generate_strings.py
│   │   │   ├── geoip.py
│   │   │   ├── gzipper.py
│   │   │   ├── hadoop_decompress.py
│   │   │   ├── hardcachebackend.py
│   │   │   ├── helpers.py
│   │   │   ├── hooks.py
│   │   │   ├── inventory.py
│   │   │   ├── inventory_optimization.py
│   │   │   ├── ip_events.py
│   │   │   ├── js.py
│   │   │   ├── jsonresponse.py
│   │   │   ├── jsontemplates.py
│   │   │   ├── language.py
│   │   │   ├── lock.py
│   │   │   ├── log.py
│   │   │   ├── loid.py
│   │   │   ├── manager/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── db_manager.py
│   │   │   │   └── tp_manager.py
│   │   │   ├── media.py
│   │   │   ├── memoize.py
│   │   │   ├── menus.py
│   │   │   ├── merge.py
│   │   │   ├── message_to_email.py
│   │   │   ├── migrate/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── campaigns_to_things.py
│   │   │   │   ├── migrate.py
│   │   │   │   ├── mr_domains.py
│   │   │   │   ├── mr_permacache.py
│   │   │   │   └── vote_details_ip_backfill.py
│   │   │   ├── mr_tools/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── _mr_tools.pyx
│   │   │   │   └── mr_tools.py
│   │   │   ├── mr_top.py
│   │   │   ├── newsletter.py
│   │   │   ├── normalized_hot.py
│   │   │   ├── nymph.py
│   │   │   ├── organic.py
│   │   │   ├── pages/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── admin_pages.py
│   │   │   │   ├── pages.py
│   │   │   │   ├── things.py
│   │   │   │   ├── trafficpages.py
│   │   │   │   └── wiki.py
│   │   │   ├── permissions.py
│   │   │   ├── plugin.py
│   │   │   ├── profiler.py
│   │   │   ├── promote.py
│   │   │   ├── providers/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── auth/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── cookie.py
│   │   │   │   │   └── http.py
│   │   │   │   ├── cdn/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── cloudflare.py
│   │   │   │   │   ├── fastly.py
│   │   │   │   │   └── null.py
│   │   │   │   ├── email/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── mailgun.py
│   │   │   │   │   └── null.py
│   │   │   │   ├── image_resizing/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── imgix.py
│   │   │   │   │   ├── no_op.py
│   │   │   │   │   └── unsplashit.py
│   │   │   │   ├── media/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── filesystem.py
│   │   │   │   │   └── s3.py
│   │   │   │   ├── search/
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── cloudsearch.py
│   │   │   │   │   ├── common.py
│   │   │   │   │   └── solr.py
│   │   │   │   └── support/
│   │   │   │       ├── __init__.py
│   │   │   │       └── zendesk.py
│   │   │   ├── ratelimit.py
│   │   │   ├── recommender.py
│   │   │   ├── require.py
│   │   │   ├── rising.py
│   │   │   ├── s3_helpers.py
│   │   │   ├── sgm.pyx
│   │   │   ├── signing.py
│   │   │   ├── sitemaps/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── data.py
│   │   │   │   ├── generate.py
│   │   │   │   ├── store.py
│   │   │   │   └── watcher.py
│   │   │   ├── souptest.py
│   │   │   ├── sr_pops.py
│   │   │   ├── static.py
│   │   │   ├── stats.py
│   │   │   ├── strings.py
│   │   │   ├── subreddit_search.py
│   │   │   ├── support_tickets.py
│   │   │   ├── system_messages.py
│   │   │   ├── takedowns.py
│   │   │   ├── template_helpers.py
│   │   │   ├── totp.py
│   │   │   ├── tracking.py
│   │   │   ├── traffic/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── emr_traffic.py
│   │   │   │   └── traffic.py
│   │   │   ├── translation.py
│   │   │   ├── trending.py
│   │   │   ├── unicode.py
│   │   │   ├── utils/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── _utils.pyx
│   │   │   │   ├── comment_tree_utils.pyx
│   │   │   │   ├── feature_utils.py
│   │   │   │   ├── http_utils.py
│   │   │   │   ├── reddit_agent_parser.py
│   │   │   │   └── utils.py
│   │   │   ├── validator/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── preferences.py
│   │   │   │   ├── validator.py
│   │   │   │   └── wiki.py
│   │   │   ├── voting.py
│   │   │   ├── websockets.py
│   │   │   ├── wrapped.pyx
│   │   │   └── zookeeper.py
│   │   ├── models/
│   │   │   ├── __init__.py
│   │   │   ├── account.py
│   │   │   ├── admin_notes.py
│   │   │   ├── admintools.py
│   │   │   ├── automoderator.py
│   │   │   ├── award.py
│   │   │   ├── bans.py
│   │   │   ├── bidding.py
│   │   │   ├── builder.py
│   │   │   ├── comment_tree.py
│   │   │   ├── flair.py
│   │   │   ├── gold.py
│   │   │   ├── ip.py
│   │   │   ├── keyvalue.py
│   │   │   ├── last_modified.py
│   │   │   ├── link.py
│   │   │   ├── listing.py
│   │   │   ├── mail_queue.py
│   │   │   ├── media_cache.py
│   │   │   ├── modaction.py
│   │   │   ├── printable.py
│   │   │   ├── promo.py
│   │   │   ├── promo_metrics.py
│   │   │   ├── query_cache.py
│   │   │   ├── recommend.py
│   │   │   ├── report.py
│   │   │   ├── rules.py
│   │   │   ├── subreddit.py
│   │   │   ├── token.py
│   │   │   ├── traffic.py
│   │   │   ├── trylater.py
│   │   │   ├── vote.py
│   │   │   └── wiki.py
│   │   ├── public/
│   │   │   └── static/
│   │   │       ├── button/
│   │   │       │   ├── button-embed.js
│   │   │       │   ├── button1.html
│   │   │       │   ├── button1.js
│   │   │       │   ├── button2.html
│   │   │       │   ├── button2.js
│   │   │       │   ├── button3.html
│   │   │       │   └── button3.js
│   │   │       ├── css/
│   │   │       │   ├── adminbar.less
│   │   │       │   ├── compact.css
│   │   │       │   ├── compact.scss
│   │   │       │   ├── components/
│   │   │       │   │   ├── alerts.less
│   │   │       │   │   ├── animations.less
│   │   │       │   │   ├── buttons.less
│   │   │       │   │   ├── close.less
│   │   │       │   │   ├── colors.less
│   │   │       │   │   ├── components.less
│   │   │       │   │   ├── form-states.less
│   │   │       │   │   ├── forms.less
│   │   │       │   │   ├── image-upload.less
│   │   │       │   │   ├── infobar.less
│   │   │       │   │   ├── mixins.less
│   │   │       │   │   ├── modal.less
│   │   │       │   │   ├── progress.less
│   │   │       │   │   ├── read-next.less
│   │   │       │   │   ├── strength-meter.less
│   │   │       │   │   ├── toggles.less
│   │   │       │   │   ├── tooltip.less
│   │   │       │   │   ├── utils.less
│   │   │       │   │   └── variables.less
│   │   │       │   ├── config.rb
│   │   │       │   ├── expando.less
│   │   │       │   ├── highlight.css
│   │   │       │   ├── interstitial.less
│   │   │       │   ├── markdown.less
│   │   │       │   ├── mobile.css
│   │   │       │   ├── mod-action-icons.less
│   │   │       │   ├── modtools.less
│   │   │       │   ├── policies.less
│   │   │       │   ├── post-sharing.less
│   │   │       │   ├── reddit-embed.less
│   │   │       │   ├── reddit.less
│   │   │       │   ├── search.less
│   │   │       │   ├── subreddit-rules.less
│   │   │       │   └── wiki.less
│   │   │       ├── inbound-email-policy.html
│   │   │       ├── js/
│   │   │       │   ├── access.js
│   │   │       │   ├── action-forms.js
│   │   │       │   ├── actions.js
│   │   │       │   ├── adminbar.js
│   │   │       │   ├── ajax.js
│   │   │       │   ├── analytics.js
│   │   │       │   ├── apps.js
│   │   │       │   ├── archived.js
│   │   │       │   ├── backbone-init.js
│   │   │       │   ├── base.js
│   │   │       │   ├── bootstrap.tooltip.extension.js
│   │   │       │   ├── cache-poisoning-detection.js
│   │   │       │   ├── client-error-logger.js
│   │   │       │   ├── compact.js
│   │   │       │   ├── custom-event.js
│   │   │       │   ├── do-not-track.js
│   │   │       │   ├── edit-subreddit-rules.js
│   │   │       │   ├── embed/
│   │   │       │   │   ├── comment-embed.js
│   │   │       │   │   ├── embed.js
│   │   │       │   │   ├── pixel-tracking.js
│   │   │       │   │   └── utils.js
│   │   │       │   ├── embed.js
│   │   │       │   ├── errors.js
│   │   │       │   ├── events.js
│   │   │       │   ├── expando/
│   │   │       │   │   ├── nsfwflow.js
│   │   │       │   │   └── nsfwgate.html
│   │   │       │   ├── expando.js
│   │   │       │   ├── filter.js
│   │   │       │   ├── flair.js
│   │   │       │   ├── frames.js
│   │   │       │   ├── gate-popup.js
│   │   │       │   ├── gold.js
│   │   │       │   ├── google-tag-manager/
│   │   │       │   │   ├── gtm-jail-listener.js
│   │   │       │   │   ├── gtm-listener.js
│   │   │       │   │   └── gtm.js
│   │   │       │   ├── highlight.js
│   │   │       │   ├── hooks.js
│   │   │       │   ├── https-tester.js
│   │   │       │   ├── i18n.js
│   │   │       │   ├── image-upload.js
│   │   │       │   ├── interestbar.js
│   │   │       │   ├── jail.js
│   │   │       │   ├── jquery.reddit.js
│   │   │       │   ├── lib/
│   │   │       │   │   ├── backbone-1.0.0.js
│   │   │       │   │   ├── bootstrap.modal.js
│   │   │       │   │   ├── bootstrap.tooltip.js
│   │   │       │   │   ├── bootstrap.transition.js
│   │   │       │   │   ├── es5-sham.js
│   │   │       │   │   ├── es5-shim.js
│   │   │       │   │   ├── event-tracker.js
│   │   │       │   │   ├── highlight.pack.js
│   │   │       │   │   ├── hmac-sha256.js
│   │   │       │   │   ├── html5shiv.js
│   │   │       │   │   ├── jed.js
│   │   │       │   │   ├── jquery-1.11.1.js
│   │   │       │   │   ├── jquery-2.1.1.js
│   │   │       │   │   ├── jquery.cookie.js
│   │   │       │   │   ├── jquery.flot.js
│   │   │       │   │   ├── jquery.flot.time.js
│   │   │       │   │   ├── jquery.lazyload.js
│   │   │       │   │   ├── jquery.url.js
│   │   │       │   │   ├── json2.js
│   │   │       │   │   ├── less-1.4.2.js
│   │   │       │   │   ├── modernizr.js
│   │   │       │   │   ├── react-with-addons-0.11.2.js
│   │   │       │   │   ├── reddit-client-lib.js
│   │   │       │   │   ├── store.js
│   │   │       │   │   ├── ui.core.js
│   │   │       │   │   ├── ui.datepicker.js
│   │   │       │   │   └── underscore-1.4.4-1.js
│   │   │       │   ├── link-click-tracking.js
│   │   │       │   ├── locked.js
│   │   │       │   ├── logging.js
│   │   │       │   ├── login.js
│   │   │       │   ├── messagecompose.js
│   │   │       │   ├── messages.js
│   │   │       │   ├── migrate-global-reddit.js
│   │   │       │   ├── models/
│   │   │       │   │   ├── subreddit-rule.js
│   │   │       │   │   └── validators.js
│   │   │       │   ├── multi.js
│   │   │       │   ├── newsletter.js
│   │   │       │   ├── policies.js
│   │   │       │   ├── popup.js
│   │   │       │   ├── post-sharing.js
│   │   │       │   ├── preload.js
│   │   │       │   ├── qrcode.js
│   │   │       │   ├── recommender.js
│   │   │       │   ├── reddit-hook.js
│   │   │       │   ├── reddit-init-hook.js
│   │   │       │   ├── reddit.js
│   │   │       │   ├── report.js
│   │   │       │   ├── safe-store.js
│   │   │       │   ├── saved.js
│   │   │       │   ├── scrollupdater.js
│   │   │       │   ├── setup.js
│   │   │       │   ├── sponsored.js
│   │   │       │   ├── spotlight.js
│   │   │       │   ├── sr-autocomplete.js
│   │   │       │   ├── stateify.js
│   │   │       │   ├── strength-meter.js
│   │   │       │   ├── synced-session-storage.js
│   │   │       │   ├── templates.js
│   │   │       │   ├── timeouts.js
│   │   │       │   ├── timeseries.js
│   │   │       │   ├── timetext.js
│   │   │       │   ├── timings.js
│   │   │       │   ├── toggles.js
│   │   │       │   ├── traffic.js
│   │   │       │   ├── ui/
│   │   │       │   │   ├── formbar.html
│   │   │       │   │   └── formbar.js
│   │   │       │   ├── ui.js
│   │   │       │   ├── uibase.js
│   │   │       │   ├── utils.js
│   │   │       │   ├── uuid.js
│   │   │       │   ├── validator.js
│   │   │       │   ├── visited.js
│   │   │       │   ├── voting.js
│   │   │       │   ├── warn-on-unload.js
│   │   │       │   ├── websocket.js
│   │   │       │   └── wiki.js
│   │   │       ├── opensearch.xml
│   │   │       └── sureroute.html
│   │   ├── templates/
│   │   │   ├── __init__.py
│   │   │   ├── accountactivitybox.html
│   │   │   ├── adminawardgive.html
│   │   │   ├── adminawards.html
│   │   │   ├── adminawardwinners.html
│   │   │   ├── adminbar.html
│   │   │   ├── admincreddits.html
│   │   │   ├── adminerrorlog.html
│   │   │   ├── admingold.html
│   │   │   ├── admininterstitial.html
│   │   │   ├── adminnotessidebar.html
│   │   │   ├── ads.html
│   │   │   ├── adverttrafficsummary.html
│   │   │   ├── allinfobar.html
│   │   │   ├── apihelp.html
│   │   │   ├── archivedinterstitial.html
│   │   │   ├── automoderatorconfig.html
│   │   │   ├── awardreceived.html
│   │   │   ├── bannedinterstitial.html
│   │   │   ├── banneduserinterstitial.html
│   │   │   ├── base.compact
│   │   │   ├── base.html
│   │   │   ├── base.htmllite
│   │   │   ├── base.iframe
│   │   │   ├── base.mobile
│   │   │   ├── base.xml
│   │   │   ├── buttondemopanel.html
│   │   │   ├── buttonlite.js
│   │   │   ├── captcha.compact
│   │   │   ├── captcha.html
│   │   │   ├── clickgadget.html
│   │   │   ├── clientinfobar.compact
│   │   │   ├── clientinfobar.html
│   │   │   ├── comment.compact
│   │   │   ├── comment.html
│   │   │   ├── comment.htmllite
│   │   │   ├── comment.iframe
│   │   │   ├── comment.mobile
│   │   │   ├── comment.xml
│   │   │   ├── comment_skeleton.html
│   │   │   ├── commentvisitsbox.html
│   │   │   ├── commentvisitsbox.xml
│   │   │   ├── confirmawardclaim.html
│   │   │   ├── contactus.html
│   │   │   ├── createsubreddit.html
│   │   │   ├── creditgild.html
│   │   │   ├── crossdomain.xml
│   │   │   ├── csserror.html
│   │   │   ├── debugfooter.html
│   │   │   ├── deleteduserinterstitial.html
│   │   │   ├── emailchangeemail.email
│   │   │   ├── embed.html
│   │   │   ├── errorpage.compact
│   │   │   ├── errorpage.html
│   │   │   ├── exploreitem.html
│   │   │   ├── exploreitemlisting.html
│   │   │   ├── filteredinfobar.html
│   │   │   ├── flairlist.html
│   │   │   ├── flairlistrow.html
│   │   │   ├── flairnextlink.html
│   │   │   ├── flairpane.html
│   │   │   ├── flairprefs.html
│   │   │   ├── flairselector.html
│   │   │   ├── flairselectorlinksample.html
│   │   │   ├── flairtemplateeditor.html
│   │   │   ├── flairtemplatelist.html
│   │   │   ├── flairtemplatesample.html
│   │   │   ├── fraudform.html
│   │   │   ├── geotargetnotice.html
│   │   │   ├── gettextheader.html
│   │   │   ├── gilding.html
│   │   │   ├── gold.html
│   │   │   ├── goldgiftcodeemail.email
│   │   │   ├── goldonlyinterstitial.html
│   │   │   ├── goldpayment.compact
│   │   │   ├── goldpayment.html
│   │   │   ├── goldpayment.htmllite
│   │   │   ├── goldpayment.mobile
│   │   │   ├── goldpayment.xml
│   │   │   ├── goldsubscription.html
│   │   │   ├── goldthanks.html
│   │   │   ├── goldvertisement.html
│   │   │   ├── googletagmanager.html
│   │   │   ├── googletagmanagerjail.html
│   │   │   ├── headerbar.mobile
│   │   │   ├── helplink.html
│   │   │   ├── helppage.html
│   │   │   ├── infobar.html
│   │   │   ├── interestbar.html
│   │   │   ├── interstitial.html
│   │   │   ├── intimeoutinterstitial.html
│   │   │   ├── languagetrafficsummary.html
│   │   │   ├── less.html
│   │   │   ├── link.compact
│   │   │   ├── link.html
│   │   │   ├── link.htmllite
│   │   │   ├── link.mobile
│   │   │   ├── link.xml
│   │   │   ├── linkcommentsep.mobile
│   │   │   ├── linkcommentssettings.compact
│   │   │   ├── linkcommentssettings.html
│   │   │   ├── linkinfobar.html
│   │   │   ├── linkinfopage.htmllite
│   │   │   ├── linkinfopage.iframe
│   │   │   ├── linklisting.html
│   │   │   ├── listing.compact
│   │   │   ├── listing.html
│   │   │   ├── listing.htmllite
│   │   │   ├── listing.iframe
│   │   │   ├── listing.mobile
│   │   │   ├── listing.xml
│   │   │   ├── listingchooser.html
│   │   │   ├── listingsuggestions.html
│   │   │   ├── locationbar.html
│   │   │   ├── lockedinterstitial.html
│   │   │   ├── login.compact
│   │   │   ├── login.html
│   │   │   ├── loginformwide.html
│   │   │   ├── mail_opt.email
│   │   │   ├── mediaembed.html
│   │   │   ├── mediaembedbody.html
│   │   │   ├── mediapreview.html
│   │   │   ├── menuarea.compact
│   │   │   ├── menuarea.html
│   │   │   ├── menulink.compact
│   │   │   ├── menulink.html
│   │   │   ├── message.compact
│   │   │   ├── message.html
│   │   │   ├── message.xml
│   │   │   ├── messagecompose.compact
│   │   │   ├── messagecompose.html
│   │   │   ├── messagenotificationemail.email
│   │   │   ├── messagenotificationemailsunsubscribe.html
│   │   │   ├── mobilewebredirectbar.compact
│   │   │   ├── modaction.html
│   │   │   ├── modaction.xml
│   │   │   ├── moderatormessagecompose.html
│   │   │   ├── moderatorpermissions.html
│   │   │   ├── modlisting.html
│   │   │   ├── modsrinfobar.html
│   │   │   ├── modtoolspage.html
│   │   │   ├── morechildren.compact
│   │   │   ├── morechildren.html
│   │   │   ├── moremessages.html
│   │   │   ├── morerecursion.compact
│   │   │   ├── morerecursion.html
│   │   │   ├── morerecursion.iframe
│   │   │   ├── multiinfobar.html
│   │   │   ├── navbutton.compact
│   │   │   ├── navbutton.html
│   │   │   ├── navbutton.mobile
│   │   │   ├── navmenu.compact
│   │   │   ├── navmenu.html
│   │   │   ├── navmenu.mobile
│   │   │   ├── newlink.compact
│   │   │   ├── newlink.html
│   │   │   ├── newsletter.html
│   │   │   ├── newsletterbar.html
│   │   │   ├── oauth2authorization.compact
│   │   │   ├── oauth2authorization.html
│   │   │   ├── optout.html
│   │   │   ├── over18interstitial.html
│   │   │   ├── pagenamenav.compact
│   │   │   ├── pagenamenav.html
│   │   │   ├── pagenamenav.mobile
│   │   │   ├── panestack.compact
│   │   │   ├── panestack.html
│   │   │   ├── panestack.htmllite
│   │   │   ├── panestack.iframe
│   │   │   ├── panestack.mobile
│   │   │   ├── panestack.xml
│   │   │   ├── password.html
│   │   │   ├── passwordchangeemail.email
│   │   │   ├── passwordreset.email
│   │   │   ├── paymentform.html
│   │   │   ├── permalinkmessage.html
│   │   │   ├── policypage.html
│   │   │   ├── policyview.html
│   │   │   ├── popup.html
│   │   │   ├── prefapps.html
│   │   │   ├── prefdeactivate.html
│   │   │   ├── preffeeds.html
│   │   │   ├── prefoptions.html
│   │   │   ├── prefsecurity.html
│   │   │   ├── prefupdate.html
│   │   │   ├── printable.compact
│   │   │   ├── printable.html
│   │   │   ├── printable.htmllite
│   │   │   ├── printable.iframe
│   │   │   ├── printable.mobile
│   │   │   ├── printablebuttons.html
│   │   │   ├── privateinterstitial.html
│   │   │   ├── profilebar.html
│   │   │   ├── promo_email.email
│   │   │   ├── promotedlink.html
│   │   │   ├── promotedlinktraffic.html
│   │   │   ├── promoteinventory.html
│   │   │   ├── promotelinkbase.html
│   │   │   ├── promotelinkedit.html
│   │   │   ├── promotelinknew.html
│   │   │   ├── promotereport.html
│   │   │   ├── quarantineinterstitial.html
│   │   │   ├── ratelimit_base.html
│   │   │   ├── ratelimit_throttled.html
│   │   │   ├── ratelimit_toofast.html
│   │   │   ├── rawcode.html
│   │   │   ├── readnext.html
│   │   │   ├── readnextlink.html
│   │   │   ├── readnextlisting.html
│   │   │   ├── reddit.compact
│   │   │   ├── reddit.html
│   │   │   ├── reddit.htmllite
│   │   │   ├── reddit.mobile
│   │   │   ├── reddit.xml
│   │   │   ├── redditfooter.html
│   │   │   ├── redditheader.compact
│   │   │   ├── redditheader.html
│   │   │   ├── redditheader.mobile
│   │   │   ├── redditinfobar.html
│   │   │   ├── reddittraffic.html
│   │   │   ├── refundpage.html
│   │   │   ├── register.compact
│   │   │   ├── registrationinfo.html
│   │   │   ├── renderablecampaign.html
│   │   │   ├── reportform.html
│   │   │   ├── reportformtemplates.html
│   │   │   ├── resetpassword.html
│   │   │   ├── robots.txt
│   │   │   ├── rules.html
│   │   │   ├── searchbar.compact
│   │   │   ├── searchbar.html
│   │   │   ├── searchform.compact
│   │   │   ├── searchform.html
│   │   │   ├── searchlisting.html
│   │   │   ├── searchresultbase.html
│   │   │   ├── searchresultlink.html
│   │   │   ├── searchresultsubreddit.html
│   │   │   ├── selftext.html
│   │   │   ├── serversecondsbar.html
│   │   │   ├── share.email
│   │   │   ├── shareclose.html
│   │   │   ├── sidebarmessage.html
│   │   │   ├── sidebarmodlist.html
│   │   │   ├── sidebarmultilist.html
│   │   │   ├── sidebox.html
│   │   │   ├── sidecontentbox.html
│   │   │   ├── sitewidetraffic.html
│   │   │   ├── sitewidetrafficpage.html
│   │   │   ├── sponsorlookupuser.html
│   │   │   ├── sponsorshipbox.html
│   │   │   ├── sponsorsidebar.html
│   │   │   ├── spotlightlisting.html
│   │   │   ├── starkcomment.html
│   │   │   ├── subreddit.compact
│   │   │   ├── subreddit.html
│   │   │   ├── subreddit.mobile
│   │   │   ├── subreddit.xml
│   │   │   ├── subredditfacets.html
│   │   │   ├── subredditinfobar.html
│   │   │   ├── subredditreportform.html
│   │   │   ├── subredditselector.html
│   │   │   ├── subredditstylesheet.html
│   │   │   ├── subredditstylesheetbase.html
│   │   │   ├── subredditstylesheetsource.html
│   │   │   ├── subreddittopbar.html
│   │   │   ├── subreddittraffic.html
│   │   │   ├── subreddittrafficreport.html
│   │   │   ├── subscribebutton.html
│   │   │   ├── subscriptionbox.html
│   │   │   ├── suspiciouspaymentemail.email
│   │   │   ├── tabbedpane.html
│   │   │   ├── tablelisting.html
│   │   │   ├── takedownpane.compact
│   │   │   ├── takedownpane.html
│   │   │   ├── thanks.html
│   │   │   ├── thingupdater.html
│   │   │   ├── timeserieschart.html
│   │   │   ├── trafficpage.html
│   │   │   ├── trendingsubredditsbar.html
│   │   │   ├── trophycase.html
│   │   │   ├── trycompact.compact
│   │   │   ├── unreadmessagessuggestions.html
│   │   │   ├── uploadedadsimage.html
│   │   │   ├── uploadedimage.html
│   │   │   ├── userawards.html
│   │   │   ├── userblockedinterstitial.html
│   │   │   ├── useriphistory.html
│   │   │   ├── userlisting.html
│   │   │   ├── usertableitem.html
│   │   │   ├── usertext.compact
│   │   │   ├── usertext.html
│   │   │   ├── usertext.mobile
│   │   │   ├── utils/
│   │   │   │   └── gold.html
│   │   │   ├── utils.compact
│   │   │   ├── utils.html
│   │   │   ├── utils.xml
│   │   │   ├── verifyemail.email
│   │   │   ├── welcomebar.html
│   │   │   ├── widgetdemopanel.html
│   │   │   ├── wikibasepage.html
│   │   │   ├── wikieditpage.html
│   │   │   ├── wikipagediscussions.html
│   │   │   ├── wikipagediscussions.xml
│   │   │   ├── wikipagelisting.html
│   │   │   ├── wikipagenotfound.html
│   │   │   ├── wikipagerevisions.html
│   │   │   ├── wikipagerevisions.xml
│   │   │   ├── wikipagesettings.html
│   │   │   ├── wikirevision.html
│   │   │   ├── wikirevision.xml
│   │   │   ├── wikiview.compact
│   │   │   ├── wikiview.html
│   │   │   ├── wrappeduser.compact
│   │   │   ├── wrappeduser.html
│   │   │   └── wrappeduser.mobile
│   │   └── tests/
│   │       ├── __init__.py
│   │       ├── functional/
│   │       │   ├── __init__.py
│   │       │   └── controller/
│   │       │       ├── __init__.py
│   │       │       ├── del_msg_test.py
│   │       │       ├── login/
│   │       │       │   ├── __init__.py
│   │       │       │   ├── api_tests.py
│   │       │       │   ├── apiv1_tests.py
│   │       │       │   ├── common.py
│   │       │       │   └── post_tests.py
│   │       │       └── prefs/
│   │       │           └── __init__.py
│   │       └── unit/
│   │           ├── __init__.py
│   │           ├── config/
│   │           │   ├── __init__.py
│   │           │   ├── experiment_test.py
│   │           │   └── feature_test.py
│   │           ├── lib/
│   │           │   ├── __init__.py
│   │           │   ├── authorize/
│   │           │   │   ├── __init__.py
│   │           │   │   ├── test_api.py
│   │           │   │   └── test_interaction.py
│   │           │   ├── configparse_test.py
│   │           │   ├── cookie_upgrade_test.py
│   │           │   ├── cssfilter_test.py
│   │           │   ├── eventcollector_tests.py
│   │           │   ├── js_test.py
│   │           │   ├── loid_tests.py
│   │           │   ├── media_test.py
│   │           │   ├── permissions_test.py
│   │           │   ├── promote_test.py
│   │           │   ├── providers/
│   │           │   │   ├── __init__.py
│   │           │   │   └── image_resizing/
│   │           │   │       ├── __init__.py
│   │           │   │       ├── imgix_test.py
│   │           │   │       ├── no_op_test.py
│   │           │   │       └── unsplashit_test.py
│   │           │   ├── reddit_agent_parser_test.py
│   │           │   ├── signing_tests.py
│   │           │   ├── souptest_test.py
│   │           │   ├── stats_test.py
│   │           │   ├── tracking_test.py
│   │           │   ├── urlparser_test.py
│   │           │   ├── utils_test.py
│   │           │   └── validator/
│   │           │       ├── __init__.py
│   │           │       ├── test_validator.py
│   │           │       └── test_vverifypassword.py
│   │           ├── models/
│   │           │   ├── __init__.py
│   │           │   ├── commentbuilder_test.py
│   │           │   ├── link_test.py
│   │           │   ├── promo_test.py
│   │           │   ├── subreddit_test.py
│   │           │   ├── thing_test.py
│   │           │   ├── user_message_builder_test.py
│   │           │   └── vote_test.py
│   │           └── ratelimit_test.py
│   ├── setup.cfg
│   ├── setup.py
│   └── updateini.py
├── scripts/
│   ├── add_to_collection
│   ├── compute_time_listings
│   ├── geoip_service.py
│   ├── hashdist.py
│   ├── inject_test_data.py
│   ├── manage-consumers
│   ├── migrate/
│   │   ├── backfill/
│   │   │   ├── comment_scores_by_link.py
│   │   │   ├── fix_preview_images.py
│   │   │   ├── gilded_by_subreddit.py
│   │   │   ├── gilded_comments.py
│   │   │   ├── gilded_user_comments.py
│   │   │   ├── modaction_by_srandmod.py
│   │   │   ├── modmsgtime.py
│   │   │   ├── msgtime_to_inbox_count.py
│   │   │   ├── num_gildings.py
│   │   │   ├── scrub_deleted_users.py
│   │   │   ├── srmember_to_cassandra.py
│   │   │   ├── subreddit_images.py
│   │   │   └── user_gildings.py
│   │   ├── comment-participation.pig
│   │   ├── dump-all.sh
│   │   ├── dump-rel.sh
│   │   ├── dump-thing.sh
│   │   ├── example.sh
│   │   ├── regenerate-query-cache.py
│   │   ├── run-query.sh
│   │   ├── tuples_to_sstables.py
│   │   └── udfs/
│   │       ├── build.xml
│   │       └── src/
│   │           └── com/
│   │               └── reddit/
│   │                   └── pig/
│   │                       ├── MAKE_FULLNAME.java
│   │                       ├── MAKE_MAP.java
│   │                       ├── MAKE_ROWKEY.java
│   │                       ├── MAKE_THING2_FULLNAME.java
│   │                       ├── TO_36.java
│   │                       ├── TO_JSON.java
│   │                       └── TypeID.java
│   ├── promoted_links.py
│   ├── read_secrets
│   ├── saferun.sh
│   ├── stylecheck_git_diff.sh
│   ├── tracker.py
│   ├── traffic/
│   │   ├── Makefile
│   │   ├── decrypt_userinfo.c
│   │   ├── mr_aggregate.pig
│   │   ├── mr_coalesce.pig
│   │   ├── mr_process_hour.pig
│   │   ├── parse.c
│   │   ├── traffic_bootstrap.sh
│   │   ├── utils.c
│   │   ├── utils.h
│   │   └── verify.c
│   ├── upload_static_files_to_s3.py
│   ├── wrap-job
│   ├── write_live_config
│   └── write_secrets
├── solr/
│   ├── README.md
│   ├── schema.xml
│   └── schema4.xml
└── upstart/
    ├── reddit-boot.conf
    ├── reddit-consumer-author_query_q.conf
    ├── reddit-consumer-automoderator_q.conf
    ├── reddit-consumer-butler_q.conf
    ├── reddit-consumer-commentstree_q.conf
    ├── reddit-consumer-del_account_q.conf
    ├── reddit-consumer-domain_query_q.conf
    ├── reddit-consumer-event_collector_q.conf
    ├── reddit-consumer-markread_q.conf
    ├── reddit-consumer-modmail_email_q.conf
    ├── reddit-consumer-newcomments_q.conf
    ├── reddit-consumer-scraper_q.conf
    ├── reddit-consumer-search_q.conf
    ├── reddit-consumer-sitemaps_q.conf
    ├── reddit-consumer-subreddit_query_q.conf
    ├── reddit-consumer-vote_comment_q.conf
    ├── reddit-consumer-vote_link_q.conf
    ├── reddit-consumers-restart.conf
    ├── reddit-consumers-start.conf
    ├── reddit-job-broken_things.conf
    ├── reddit-job-clean_up_hardcache.conf
    ├── reddit-job-email.conf
    ├── reddit-job-hourly_traffic.conf
    ├── reddit-job-rising.conf
    ├── reddit-job-subscribers.conf
    ├── reddit-job-trylater.conf
    ├── reddit-job-update_geoip.conf
    ├── reddit-job-update_gold_users.conf
    ├── reddit-job-update_popular_subreddits.conf
    ├── reddit-job-update_promo_metrics.conf
    ├── reddit-job-update_promos.conf
    ├── reddit-job-update_reddits.conf
    ├── reddit-job-update_sr_names.conf
    ├── reddit-job-update_trending_subreddits.conf
    └── reddit-paster.conf
Download .txt
Showing preview only (564K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (7491 symbols across 341 files)

FILE: r2/r2/__init__.py
  function make_app (line 39) | def make_app(*args, **kwargs):

FILE: r2/r2/commands.py
  class RunCommand (line 32) | class RunCommand(command.Command):
    method command (line 49) | def command(self):

FILE: r2/r2/config/environment.py
  function load_environment (line 44) | def load_environment(global_conf={}, app_conf={}, setup_globals=True):

FILE: r2/r2/config/extensions.py
  function api_type (line 25) | def api_type(subtype = ''):
  function is_api (line 28) | def is_api(subtype = ''):
  function get_api_subtype (line 31) | def get_api_subtype():
  function set_extension (line 55) | def set_extension(environ, ext):

FILE: r2/r2/config/feature/feature.py
  function is_enabled (line 33) | def is_enabled(name, user=None, subreddit=None):
  function variant (line 60) | def variant(name, user=None):
  function all_enabled (line 76) | def all_enabled(user=None):
  function clear_featurestate_cache (line 116) | def clear_featurestate_cache():
  function _get_featurestate (line 121) | def _get_featurestate(name):

FILE: r2/r2/config/feature/state.py
  class FeatureState (line 31) | class FeatureState(object):
    method __init__ (line 54) | def __init__(self, name, world, config_name=None, config_str=None):
    method _parse_config (line 59) | def _parse_config(self, name, config_name=None, config_str=None):
    method get_all (line 92) | def get_all(world):
    method is_user_experiment (line 108) | def is_user_experiment(experiment):
    method is_page_experiment (line 112) | def is_page_experiment(experiment):
    method _calculate_bucket (line 115) | def _calculate_bucket(self, seed, experiment_seed=None):
    method _choose_variant (line 130) | def _choose_variant(cls, bucket, variants):
    method _is_variant_enabled (line 211) | def _is_variant_enabled(cls, variant):
    method is_enabled (line 223) | def is_enabled(self, user=None, subreddit=None, subdomain=None,
    method _is_config_enabled (line 253) | def _is_config_enabled(
    method _is_percent_enabled (line 312) | def _is_percent_enabled(self, cfg, user=None):
    method _is_experiment_enabled (line 335) | def _is_experiment_enabled(self, experiment, user=None):
    method variant (line 397) | def variant(self, user):
    method _get_user_experiment_variant (line 420) | def _get_user_experiment_variant(self, experiment, user):
    method get_content_id (line 439) | def get_content_id():
    method _get_page_experiment_variant (line 459) | def _get_page_experiment_variant(self, experiment):

FILE: r2/r2/config/feature/world.py
  class World (line 28) | class World(object):
    method stacked_proxy_safe_get (line 35) | def stacked_proxy_safe_get(stacked_proxy, key, default=None):
    method current_user (line 54) | def current_user(self):
    method current_subreddit (line 58) | def current_subreddit(self):
    method current_subdomain (line 67) | def current_subdomain(self):
    method current_oauth_client (line 70) | def current_oauth_client(self):
    method current_loid_obj (line 74) | def current_loid_obj(self):
    method current_loid (line 77) | def current_loid(self):
    method is_admin (line 83) | def is_admin(self, user):
    method is_employee (line 89) | def is_employee(self, user):
    method user_has_beta_enabled (line 94) | def user_has_beta_enabled(self, user):
    method has_gold (line 99) | def has_gold(self, user):
    method is_user_loggedin (line 105) | def is_user_loggedin(self, user):
    method url_features (line 110) | def url_features(self):
    method live_config (line 113) | def live_config(self, name):
    method live_config_iteritems (line 117) | def live_config_iteritems(self):
    method simple_event (line 121) | def simple_event(self, name):

FILE: r2/r2/config/hooks.py
  function register_hooks (line 24) | def register_hooks():

FILE: r2/r2/config/middleware.py
  class HTTPTooManyRequests (line 56) | class HTTPTooManyRequests(webob.exc.HTTPClientError):
  function _stub (line 69) | def _stub(*args, **kwargs):
  function error_mapper (line 76) | def error_mapper(code, message, environ, global_conf=None, **kw):
  function ErrorDocuments (line 120) | def ErrorDocuments(app, global_conf, mapper, **kw):
  class ProfilingMiddleware (line 131) | class ProfilingMiddleware(object):
    method __init__ (line 132) | def __init__(self, app, directory):
    method __call__ (line 136) | def __call__(self, environ, start_response):
  class DomainMiddleware (line 153) | class DomainMiddleware(object):
    method __init__ (line 155) | def __init__(self, app, config):
    method __call__ (line 159) | def __call__(self, environ, start_response):
  class SubredditMiddleware (line 231) | class SubredditMiddleware(object):
    method __init__ (line 234) | def __init__(self, app):
    method __call__ (line 237) | def __call__(self, environ, start_response):
  class DomainListingMiddleware (line 246) | class DomainListingMiddleware(object):
    method __init__ (line 247) | def __init__(self, app):
    method __call__ (line 250) | def __call__(self, environ, start_response):
  class ExtensionMiddleware (line 261) | class ExtensionMiddleware(object):
    method __init__ (line 264) | def __init__(self, app):
    method __call__ (line 267) | def __call__(self, environ, start_response):
  class FullPathMiddleware (line 288) | class FullPathMiddleware(object):
    method __init__ (line 292) | def __init__(self, app):
    method __call__ (line 295) | def __call__(self, environ, start_response):
  class StaticTestMiddleware (line 302) | class StaticTestMiddleware(object):
    method __init__ (line 303) | def __init__(self, app, static_path, domain):
    method __call__ (line 308) | def __call__(self, environ, start_response):
  function _wsgi_json (line 315) | def _wsgi_json(start_response, status_int, message=""):
  class LimitUploadSize (line 330) | class LimitUploadSize(object):
    method __init__ (line 335) | def __init__(self, app, max_size=1024*500):
    method __call__ (line 339) | def __call__(self, environ, start_response):
  class CleanupMiddleware (line 384) | class CleanupMiddleware(object):
    method __init__ (line 391) | def __init__(self, app):
    method __call__ (line 394) | def __call__(self, environ, start_response):
  class SafetyMiddleware (line 408) | class SafetyMiddleware(object):
    method __init__ (line 414) | def __init__(self, app):
    method __call__ (line 417) | def __call__(self, environ, start_response):
  class RedditApp (line 428) | class RedditApp(PylonsApp):
    method __init__ (line 432) | def __init__(self, *args, **kwargs):
    method setup_app_env (line 438) | def setup_app_env(self, environ, start_response):
    method _check_csrf_prevention (line 449) | def _check_csrf_prevention(self):
    method load_controllers (line 461) | def load_controllers(self):
    method register_hooks (line 472) | def register_hooks(self):
    method find_controller (line 479) | def find_controller(self, controller_name):
  function make_app (line 487) | def make_app(global_conf, full_stack=True, **app_conf):

FILE: r2/r2/config/paths.py
  function get_r2_path (line 26) | def get_r2_path():
  function get_built_statics_path (line 34) | def get_built_statics_path():
  function get_raw_statics_path (line 40) | def get_raw_statics_path():

FILE: r2/r2/config/queues.py
  class Queues (line 29) | class Queues(dict):
    method __init__ (line 31) | def __init__(self, queues):
    method __iter__ (line 37) | def __iter__(self):
    method declare (line 42) | def declare(self, queues):
  class MessageQueue (line 51) | class MessageQueue(object):
    method __init__ (line 57) | def __init__(self, durable=True, exclusive=False,
    method _bind (line 64) | def _bind(self, routing_key):
    method __lshift__ (line 67) | def __lshift__(self, routing_keys):
  function declare_queues (line 74) | def declare_queues(g):

FILE: r2/r2/config/routing.py
  function not_in_sr (line 29) | def not_in_sr(environ, results):
  function partial_connect (line 37) | def partial_connect(mc, **override_args):
  function make_map (line 46) | def make_map(config):

FILE: r2/r2/config/templates.py
  function api (line 28) | def api(type, cls):
  function register_api_templates (line 34) | def register_api_templates(template_name, template_class):

FILE: r2/r2/controllers/__init__.py
  function get_controller (line 26) | def get_controller(name):
  function add_controller (line 35) | def add_controller(controller):
  function load_controllers (line 41) | def load_controllers():

FILE: r2/r2/controllers/admin.py
  class AdminToolController (line 27) | class AdminToolController(RedditController):
    method GET_creddits (line 32) | def GET_creddits(self, recipient):
    method GET_gold (line 39) | def GET_gold(self, recipient):

FILE: r2/r2/controllers/api.py
  class ApiminimalController (line 142) | class ApiminimalController(MinimalController):
    method POST_new_captcha (line 155) | def POST_new_captcha(self, form, jquery, *a, **kw):
  class ApiController (line 171) | class ApiController(RedditController):
    method ajax_login_redirect (line 176) | def ajax_login_redirect(self, form, jquery, dest):
    method GET_info (line 185) | def GET_info(self, things, url):
    method GET_url_info (line 210) | def GET_url_info(self, url, count, things):
    method GET_me (line 231) | def GET_me(self, responder):
    method GET_username_available (line 246) | def GET_username_available(self, responder, user):
    method POST_check_username (line 255) | def POST_check_username(self, responder, user):
    method POST_check_password (line 270) | def POST_check_password(self, responder, password):
    method POST_check_email (line 284) | def POST_check_email(self, responder, email, newsletter_subscribe, spo...
    method POST_newsletter (line 312) | def POST_newsletter(self, responder, email, source):
    method GET_needs_captcha (line 331) | def GET_needs_captcha(self, responder):
    method POST_compose (line 349) | def POST_compose(self, form, jquery, from_sr, to, subject, body):
    method GET_submit_text (line 413) | def GET_submit_text(self, responder):
    method POST_submit (line 449) | def POST_submit(self, form, jquery, url, selftext, kind, title,
    method POST_fetch_title (line 589) | def POST_fetch_title(self, form, jquery, url):
    method _login (line 605) | def _login(self, responder, user, rem = None):
    method POST_login (line 626) | def POST_login(self, form, responder, user, rem=None, **kwargs):
    method POST_register (line 655) | def POST_register(self, form, responder, name, email, password, **kwar...
    method POST_leavemoderator (line 679) | def POST_leavemoderator(self, container):
    method POST_leavecontributor (line 695) | def POST_leavecontributor(self, container):
    method check_api_friend_oauth_scope (line 734) | def check_api_friend_oauth_scope(self, type_):
    method POST_unfriend (line 755) | def POST_unfriend(self, nuser, iuser, container, type):
    method POST_setpermissions (line 872) | def POST_setpermissions(self, form, jquery, target, type_and_permissio...
    method POST_friend (line 928) | def POST_friend(self, form, jquery, friend,
    method POST_friendnote (line 1183) | def POST_friendnote(self, form, jquery, friend, note):
    method POST_relnote (line 1195) | def POST_relnote(self, form, jquery, type, user, note):
    method POST_accept_moderator_invite (line 1217) | def POST_accept_moderator_invite(self, form, jquery):
    method POST_clear_sessions (line 1246) | def POST_clear_sessions(self, form, jquery, password, dest):
    method revoke_sessions_and_login (line 1264) | def revoke_sessions_and_login(self, user, password):
    method revoke_sessions (line 1273) | def revoke_sessions(self, user):
    method POST_update_email (line 1286) | def POST_update_email(self, form, jquery, email, verify, dest):
    method POST_update_password (line 1337) | def POST_update_password(self, form, jquery, password, invalidate_oauth):
    method POST_deactivate_user (line 1373) | def POST_deactivate_user(self, form, jquery, deactivate_message, usern...
    method POST_del (line 1403) | def POST_del(self, thing):
    method POST_lock (line 1449) | def POST_lock(self, thing):
    method POST_unlock (line 1472) | def POST_unlock(self, thing):
    method POST_marknsfw (line 1495) | def POST_marknsfw(self, thing):
    method POST_unmarknsfw (line 1516) | def POST_unmarknsfw(self, thing):
    method POST_sendreplies (line 1546) | def POST_sendreplies(self, thing, state):
    method POST_rescrape (line 1563) | def POST_rescrape(self, thing):
    method POST_set_suggested_sort (line 1585) | def POST_set_suggested_sort(self, form, jquery, thing, sort, timeout):
    method POST_set_contest_mode (line 1611) | def POST_set_contest_mode(self, form, jquery, thing, state, timeout):
    method POST_set_subreddit_sticky (line 1638) | def POST_set_subreddit_sticky(self, form, jquery, thing, state, num,
    method POST_report (line 1683) | def POST_report(self, form, jquery, thing, reason, site_reason, other_...
    method POST_del_msg (line 1776) | def POST_del_msg(self, thing):
    method POST_block (line 1801) | def POST_block(self, thing):
    method POST_unblock_subreddit (line 1860) | def POST_unblock_subreddit(self, thing):
    method POST_mute_message_author (line 1880) | def POST_mute_message_author(self, message):
    method POST_unmute_message_author (line 1926) | def POST_unmute_message_author(self, message):
    method POST_editusertext (line 1957) | def POST_editusertext(self, form, jquery, item, text):
    method POST_comment (line 2043) | def POST_comment(self, commentform, jquery, parent, comment):
    method POST_share (line 2197) | def POST_share(self, shareform, jquery, share_to, message, link):
    method POST_vote (line 2301) | def POST_vote(self, direction, thing, rank):
    method POST_subreddit_stylesheet (line 2360) | def POST_subreddit_stylesheet(self, form, jquery,
    method POST_delete_sr_img (line 2448) | def POST_delete_sr_img(self, form, jquery, name):
    method POST_delete_sr_header (line 2482) | def POST_delete_sr_header(self, form, jquery):
    method POST_delete_sr_icon (line 2515) | def POST_delete_sr_icon(self, form, jquery):
    method POST_delete_sr_banner (line 2542) | def POST_delete_sr_banner(self, form, jquery):
    method GET_upload_sr_img (line 2562) | def GET_upload_sr_img(self, *a, **kw):
    method POST_upload_sr_img (line 2585) | def POST_upload_sr_img(self, file, header, name, form_id, img_type,
    method POST_site_admin (line 2749) | def POST_site_admin(self, form, jquery, name, sr, **kw):
    method POST_remove (line 3037) | def POST_remove(self, thing, spam):
    method POST_approve (line 3098) | def POST_approve(self, thing):
    method POST_ignore_reports (line 3142) | def POST_ignore_reports(self, thing):
    method POST_unignore_reports (line 3170) | def POST_unignore_reports(self, thing):
    method POST_distinguish (line 3197) | def POST_distinguish(self, form, jquery, thing, how):
    method GET_saved_categories (line 3323) | def GET_saved_categories(self, responder):
    method POST_save (line 3345) | def POST_save(self, thing, category):
    method POST_unsave (line 3371) | def POST_unsave(self, thing):
    method collapse_handler (line 3384) | def collapse_handler(self, things, collapse):
    method POST_collapse_message (line 3404) | def POST_collapse_message(self, things):
    method POST_uncollapse_message (line 3416) | def POST_uncollapse_message(self, things):
    method POST_unread_message (line 3429) | def POST_unread_message(self, things):
    method POST_read_message (line 3443) | def POST_read_message(self, things):
    method POST_read_all_messages (line 3457) | def POST_read_all_messages(self):
    method POST_hide (line 3474) | def POST_hide(self, links):
    method POST_unhide (line 3492) | def POST_unhide(self, links):
    method POST_moremessages (line 3507) | def POST_moremessages(self, form, jquery, parent):
    method GET_morechildren (line 3542) | def GET_morechildren(self, form, jquery, link, sort, children, mc_id):
    method POST_morechildren (line 3616) | def POST_morechildren(self):
    method POST_claimgold (line 3623) | def POST_claimgold(self, form, jquery, code):
    method POST_password (line 3680) | def POST_password(self, form, jquery, user):
    method POST_resetpassword (line 3716) | def POST_resetpassword(self, form, jquery, token, password):
    method POST_subscribe (line 3766) | def POST_subscribe(self, action, sr):
    method POST_quarantine (line 3807) | def POST_quarantine(self, form, jquery, subreddit, quarantine, subject...
    method POST_quarantine_optout (line 3848) | def POST_quarantine_optout(self, sr):
    method POST_quarantine_optin (line 3864) | def POST_quarantine_optin(self, sr):
    method POST_edit_error (line 3882) | def POST_edit_error(self, form, jquery, hexkey, nickname, status):
    method POST_editaward (line 3908) | def POST_editaward(self, form, jquery, award, colliding_award, codename,
    method POST_flair (line 3962) | def POST_flair(self, form, jquery, user, link, text, css_class):
    method POST_deleteflair (line 4015) | def POST_deleteflair(self, form, jquery, user):
    method POST_flaircsv (line 4037) | def POST_flaircsv(self, flair_csv):
    method POST_setflairenabled (line 4111) | def POST_setflairenabled(self, form, jquery, flair_enabled):
    method POST_flairconfig (line 4130) | def POST_flairconfig(self, form, jquery, flair_enabled, flair_position,
    method GET_flairlist (line 4165) | def GET_flairlist(self, num, after, reverse, count, user):
    method POST_flairtemplate (line 4186) | def POST_flairtemplate(self, form, jquery, flair_template, text,
    method POST_deleteflairtemplate (line 4257) | def POST_deleteflairtemplate(self, form, jquery, flair_template):
    method POST_clearflairtemplates (line 4278) | def POST_clearflairtemplates(self, form, jquery, flair_type):
    method POST_flairselector (line 4292) | def POST_flairselector(self, user, link):
    method POST_selectflair (line 4338) | def POST_selectflair(self, form, jquery, user, link, flair_template_id,
    method POST_set_sr_style_enabled (line 4428) | def POST_set_sr_style_enabled(self, form, jquery, sr_style_enabled):
    method POST_givetrophy (line 4461) | def POST_givetrophy(self, form, jquery, secret_used, award, description,
    method POST_removetrophy (line 4484) | def POST_removetrophy(self, form, jquery, secret_used, trophy):
    method POST_givecreddits (line 4506) | def POST_givecreddits(self, form, jquery, recipient, num_creddits):
    method POST_givegold (line 4525) | def POST_givegold(self, form, jquery, recipient, num_months):
    method POST_disable_ui (line 4540) | def POST_disable_ui(self, ui_elem):
    method POST_set_nsfw_media_pref (line 4550) | def POST_set_nsfw_media_pref(self, show_nsfw_media):
    method GET_gadget (line 4568) | def GET_gadget(self, form, jquery, type, links):
    method POST_search_reddit_names (line 4587) | def POST_search_reddit_names(self, responder, query, include_over_18, ...
    method GET_expando (line 4614) | def GET_expando(self, link):
    method POST_expando (line 4632) | def POST_expando(self):
    method POST_adminon (line 4644) | def POST_adminon(self, form, jquery, remember, dest):
    method POST_generate_otp_secret (line 4667) | def POST_generate_otp_secret(self, form, jquery):
    method POST_enable_otp (line 4683) | def POST_enable_otp(self, form, jquery, otp):
    method POST_disable_otp (line 4714) | def POST_disable_otp(self, form, jquery):
    method GET_subreddits_by_topic (line 4730) | def GET_subreddits_by_topic(self, responder, query):
    method POST_revokeapp (line 4782) | def POST_revokeapp(self, client):
    method POST_updateapp (line 4794) | def POST_updateapp(self, form, jquery, name, about_url, icon_url,
    method POST_adddeveloper (line 4862) | def POST_adddeveloper(self, form, jquery, client, account):
    method POST_removedeveloper (line 4897) | def POST_removedeveloper(self, form, jquery, client, account):
    method POST_deleteapp (line 4912) | def POST_deleteapp(self, client):
    method POST_setappicon (line 4923) | def POST_setappicon(self, form, jquery, client, icon_file):
    method POST_generate_payment_blob (line 4950) | def POST_generate_payment_blob(self, responder, thing, signed):
    method POST_modify_payment_blob (line 4990) | def POST_modify_payment_blob(self, responder, code, signed, message):
    method OPTIONS_request_promo (line 5002) | def OPTIONS_request_promo(self):
    method POST_request_promo (line 5015) | def POST_request_promo(self, srnames):
    method POST_set_left_bar_collapsed (line 5047) | def POST_set_left_bar_collapsed(self, responder, collapsed):
    method GET_subreddit_recommendations (line 5055) | def GET_subreddit_recommendations(self, srs, to_omit):
    method POST_rec_feedback (line 5076) | def POST_rec_feedback(self, form, jquery, action, srs):
    method POST_server_seconds_visibility (line 5091) | def POST_server_seconds_visibility(self, form, jquery, seconds_visibil...
    method POST_store_visits (line 5104) | def POST_store_visits(self, links):
    method POST_add_admin_note (line 5118) | def POST_add_admin_note(self, form, jquery, system, subject, note, aut...
    method POST_add_subreddit_rule (line 5136) | def POST_add_subreddit_rule(self, form, jquery, short_name, description,
    method POST_update_subreddit_rule (line 5166) | def POST_update_subreddit_rule(self, form, jquery, rule,
    method POST_remove_subreddit_rule (line 5200) | def POST_remove_subreddit_rule(self, form, jquery, rule):
    method GET_report_form (line 5215) | def GET_report_form(self, form, jquery, thing):
    method POST_hide_locationbar (line 5239) | def POST_hide_locationbar(self, form, jquery):
    method POST_use_global_defaults (line 5245) | def POST_use_global_defaults(self, form, jquery):

FILE: r2/r2/controllers/api_docs.py
  function api_doc (line 85) | def api_doc(section, uses_site=False, **kwargs):
  class ApidocsController (line 102) | class ApidocsController(RedditController):
    method docs_from_controller (line 104) | def docs_from_controller(controller, url_prefix='/api', oauth_only=Fal...
    method GET_docs (line 189) | def GET_docs(self, mode):

FILE: r2/r2/controllers/apiv1/gold.py
  class APIv1GoldController (line 44) | class APIv1GoldController(OAuth2OnlyController):
    method _gift_using_creddits (line 45) | def _gift_using_creddits(self, recipient, months=1, thing_fullname=None,
    method POST_gild (line 86) | def POST_gild(self, target):
    method POST_give (line 113) | def POST_give(self, user, months, timeout):

FILE: r2/r2/controllers/apiv1/login.py
  class APIv1LoginController (line 40) | class APIv1LoginController(RedditController):
    method pre (line 42) | def pre(self):
    method POST_register (line 55) | def POST_register(self, responder, name, email, password, **kwargs):
    method POST_login (line 71) | def POST_login(self, responder, user, **kwargs):
    method _login (line 80) | def _login(self, responder, user, rem=None):

FILE: r2/r2/controllers/apiv1/scopes.py
  class APIv1ScopesController (line 31) | class APIv1ScopesController(RedditController):
    method GET_scopes (line 40) | def GET_scopes(self, scope_str):

FILE: r2/r2/controllers/apiv1/user.py
  class APIv1UserController (line 56) | class APIv1UserController(OAuth2OnlyController):
    method GET_me (line 62) | def GET_me(self):
    method GET_prefs (line 77) | def GET_prefs(self, fields):
    method GET_usertrophies (line 90) | def GET_usertrophies(self, user):
    method GET_trophies (line 102) | def GET_trophies(self):
    method GET_karma (line 114) | def GET_karma(self):
    method PATCH_prefs (line 130) | def PATCH_prefs(self, validated_prefs):
    method PUT_friends (line 154) | def PUT_friends(self, friend, notes_json):
    method GET_friends (line 197) | def GET_friends(self, friend_rel):
    method DELETE_friends (line 208) | def DELETE_friends(self, friend_rel):

FILE: r2/r2/controllers/awards.py
  class AwardsController (line 30) | class AwardsController(RedditController):
    method GET_index (line 33) | def GET_index(self):
    method GET_give (line 44) | def GET_give(self, award, recipient, desc, url, hours):
    method GET_winners (line 55) | def GET_winners(self, award):

FILE: r2/r2/controllers/buttons.py
  class ButtonsController (line 33) | class ButtonsController(RedditController):
    method get_wrapped_link (line 34) | def get_wrapped_link(self, url, link = None, wrapper = None):
    method GET_button_embed (line 70) | def GET_button_embed(self, buttontype):
    method GET_button_lite (line 82) | def GET_button_lite(self, buttonimage, title, url, styled, newwindow):
    method GET_button_demo_page (line 104) | def GET_button_demo_page(self):
    method GET_widget_demo_page (line 112) | def GET_widget_demo_page(self):

FILE: r2/r2/controllers/captcha.py
  class CaptchaController (line 31) | class CaptchaController(RedditController):
    method GET_captchaimg (line 34) | def GET_captchaimg(self, iden):

FILE: r2/r2/controllers/embed.py
  function renderurl_cached (line 41) | def renderurl_cached(path):
  class EmbedController (line 56) | class EmbedController(RedditController):
    method rendercontent (line 59) | def rendercontent(self, input, fp):
    method renderurl (line 76) | def renderurl(self, override=None):
    method GET_blog (line 89) | def GET_blog(self):
    method GET_faq (line 93) | def GET_faq(self):

FILE: r2/r2/controllers/error.py
  function make_failien_url (line 88) | def make_failien_url():
  class ErrorController (line 94) | class ErrorController(RedditController):
    method check_for_bearer_token (line 106) | def check_for_bearer_token(self):
    method __before__ (line 113) | def __before__(self):
    method __after__ (line 129) | def __after__(self):
    method __call__ (line 135) | def __call__(self, environ, start_response):
    method send400 (line 142) | def send400(self):
    method send403 (line 152) | def send403(self):
    method send404 (line 163) | def send404(self):
    method send429 (line 169) | def send429(self):
    method send503 (line 183) | def send503(self):
    method GET_document (line 189) | def GET_document(self):
  function handle_awful_failure (line 271) | def handle_awful_failure(fail_text):

FILE: r2/r2/controllers/front.py
  class FrontController (line 72) | class FrontController(RedditController):
    method GET_link_id_redirect (line 77) | def GET_link_id_redirect(self, link):
    method GET_oldinfo (line 96) | def GET_oldinfo(self, article, type, dest, rest=None, comment=''):
    method GET_random (line 127) | def GET_random(self):
    method GET_details (line 165) | def GET_details(self, thing, oldid36, after, before, count, listing_on...
    method GET_explore (line 193) | def GET_explore(self):
    method GET_shirt (line 206) | def GET_shirt(self, article):
    method GET_comments (line 231) | def GET_comments(
    method _add_show_comments_link (line 538) | def _add_show_comments_link(self, array, article, num, max_comm, gold=...
    method GET_newreddit (line 558) | def GET_newreddit(self, name):
    method GET_stylesheet (line 572) | def GET_stylesheet(self):
    method GET_share_close (line 588) | def GET_share_close(self):
    method _make_moderationlog (line 595) | def _make_moderationlog(self, srs, num, after, reverse, count, mod=Non...
    method GET_moderationlog (line 615) | def GET_moderationlog(self, num, after, reverse, count, mod, action):
    method _make_spamlisting (line 703) | def _make_spamlisting(self, location, only, num, after, reverse, count):
    method _edit_normal_reddit (line 791) | def _edit_normal_reddit(self, location, created):
    method GET_spamlisting (line 847) | def GET_spamlisting(self, location, only, num, after, reverse, count,
    method GET_flairlisting (line 898) | def GET_flairlisting(self, num, after, reverse, count, name, timeout):
    method GET_editreddit (line 916) | def GET_editreddit(self, location, created):
    method GET_about (line 934) | def GET_about(self):
    method GET_sidebar (line 955) | def GET_sidebar(self):
    method GET_rules (line 962) | def GET_rules(self):
    method GET_sticky (line 992) | def GET_sticky(self, num):
    method GET_awards (line 1010) | def GET_awards(self):
    method GET_related (line 1017) | def GET_related(self, num, article, after, reverse, count):
    method GET_duplicates (line 1032) | def GET_duplicates(self, article, num, after, reverse, count):
    method GET_search_reddits (line 1059) | def GET_search_reddits(self, query, reverse, after, count, num, sort):
    method GET_search (line 1122) | def GET_search(self, query, num, reverse, after, count, sort, recent,
    method _search_builder_wrapper (line 1287) | def _search_builder_wrapper(self, q):
    method _legacy_search_builder_wrapper (line 1304) | def _legacy_search_builder_wrapper(self):
    method _search (line 1313) | def _search(self, query_obj, num, after, reverse, count=0, type=None,
    method GET_comment_by_id (line 1350) | def GET_comment_by_id(self, comment):
    method GET_submit (line 1358) | def GET_submit(self, url, title, text, selftext):
    method GET_catchall (line 1428) | def GET_catchall(self):
    method GET_traffic (line 1437) | def GET_traffic(self, link, campaign, before, after):
    method GET_site_traffic (line 1452) | def GET_site_traffic(self):
    method GET_lang_traffic (line 1456) | def GET_lang_traffic(self, langcode):
    method GET_advert_traffic (line 1460) | def GET_advert_traffic(self, code):
    method GET_subreddit_traffic_report (line 1464) | def GET_subreddit_traffic_report(self):
    method GET_account_activity (line 1472) | def GET_account_activity(self):
    method GET_contact_us (line 1475) | def GET_contact_us(self):
    method GET_goldthanks (line 1484) | def GET_goldthanks(self, vendor):
    method GET_confirm_award_claim (line 1521) | def GET_confirm_award_claim(self, token):
    method POST_claim_award (line 1534) | def POST_claim_award(self, token):
    method GET_received_award (line 1550) | def GET_received_award(self, trophy, preexisting):
    method GET_gilding (line 1554) | def GET_gilding(self):
    method _modify_hsts_grant (line 1564) | def _modify_hsts_grant(self, dest):
  class FormsController (line 1585) | class FormsController(RedditController):
    method GET_password (line 1587) | def GET_password(self):
    method GET_verify (line 1594) | def GET_verify(self, dest, reason):
    method GET_verify_email (line 1614) | def GET_verify_email(self, token, dest):
    method GET_resetpassword (line 1642) | def GET_resetpassword(self, token, key):
    method GET_unsubscribe_emails (line 1669) | def GET_unsubscribe_emails(self, user_id36, provided_mac):
    method GET_prefs (line 1691) | def GET_prefs(self, location='', verified=False):
    method GET_login (line 1724) | def GET_login(self, dest):
    method GET_register (line 1737) | def GET_register(self, dest):
    method GET_logout (line 1746) | def GET_logout(self, dest):
    method POST_logout (line 1752) | def POST_logout(self, dest):
    method GET_adminon (line 1759) | def GET_adminon(self, dest):
    method GET_adminoff (line 1771) | def GET_adminoff(self, dest):
    method _render_opt_in_out (line 1778) | def _render_opt_in_out(self, msg_hash, leave):
    method GET_optout (line 1790) | def GET_optout(self, msg_hash):
    method GET_optin (line 1798) | def GET_optin(self, msg_hash):
    method GET_try_compact (line 1806) | def GET_try_compact(self, dest):
    method GET_claim (line 1812) | def GET_claim(self, secret):
    method GET_creditgild (line 1818) | def GET_creditgild(self, passthrough):
    method GET_gold (line 1884) | def GET_gold(self, is_payment, goldtype, period, months, num_creddits,
    method GET_creddits (line 1999) | def GET_creddits(self):
    method GET_subscription (line 2007) | def GET_subscription(self):
  class FrontUnstyledController (line 2017) | class FrontUnstyledController(FrontController):

FILE: r2/r2/controllers/googletagmanager.py
  class GoogleTagManagerController (line 38) | class GoogleTagManagerController(MinimalController):
    method pre (line 39) | def pre(self):
    method GET_jail (line 52) | def GET_jail(self, container_id):
    method GET_gtm (line 58) | def GET_gtm(self, container_id):

FILE: r2/r2/controllers/health.py
  class HealthController (line 35) | class HealthController(MinimalController):
    method pre (line 36) | def pre(self):
    method post (line 39) | def post(self):
    method GET_health (line 42) | def GET_health(self):
    method GET_promohealth (line 50) | def GET_promohealth(self):
    method GET_cachehealth (line 54) | def GET_cachehealth(self):

FILE: r2/r2/controllers/ipn.py
  function generate_blob (line 88) | def generate_blob(data):
  function get_blob (line 97) | def get_blob(code):
  function update_blob (line 111) | def update_blob(code, updates=None):
  function has_blob (line 124) | def has_blob(custom):
  function dump_parameters (line 131) | def dump_parameters(parameters):
  function check_payment_status (line 135) | def check_payment_status(payment_status):
  function check_txn_type (line 160) | def check_txn_type(txn_type, psl):
  function existing_subscription (line 187) | def existing_subscription(subscr_id, paying_id, custom):
  function months_and_days_from_pennies (line 227) | def months_and_days_from_pennies(pennies, discount=False):
  function send_gift (line 244) | def send_gift(buyer, recipient, months, days, signed, giftmessage,
  function send_gold_code (line 324) | def send_gold_code(buyer, months, days,
  class IpnController (line 363) | class IpnController(RedditController):
    method POST_spendcreddits (line 369) | def POST_spendcreddits(self, form, jquery, months, passthrough):
    method POST_ipn (line 461) | def POST_ipn(self, paypal_secret, payment_status, txn_id, paying_id,
    method finish (line 532) | def finish(self, parameters, txn_id,
  class Webhook (line 648) | class Webhook(object):
    method __init__ (line 649) | def __init__(self, passthrough=None, transaction_id=None, subscr_id=None,
    method load_blob (line 668) | def load_blob(self):
    method __repr__ (line 679) | def __repr__(self):
  class GoldPaymentController (line 683) | class GoldPaymentController(RedditController):
    method POST_goldwebhook (line 691) | def POST_goldwebhook(self, secret):
    method validate_secret (line 707) | def validate_secret(self, secret):
    method process_response (line 714) | def process_response(cls):
    method process_webhook (line 718) | def process_webhook(self, event_type, webhook):
    method complete_gold_purchase (line 791) | def complete_gold_purchase(cls, webhook):
  function handle_stripe_error (line 877) | def handle_stripe_error(fn):
  class StripeController (line 899) | class StripeController(GoldPaymentController):
    method process_response (line 935) | def process_response(cls):
    method create_customer (line 1025) | def create_customer(cls, form, token, description):
    method charge_customer (line 1043) | def charge_customer(cls, form, customer, pennies, passthrough,
    method set_creditcard (line 1055) | def set_creditcard(cls, form, user, token):
    method set_subscription (line 1066) | def set_subscription(cls, form, customer, plan_id):
    method cancel_subscription (line 1072) | def cancel_subscription(cls, form, user):
    method POST_goldcharge (line 1092) | def POST_goldcharge(self, form, jquery, token, passthrough, pennies, m...
    method POST_modify_subscription (line 1166) | def POST_modify_subscription(self, form, jquery, token):
    method POST_cancel_subscription (line 1176) | def POST_cancel_subscription(self, form, jquery, user):
  class CoinbaseController (line 1185) | class CoinbaseController(GoldPaymentController):
    method process_response (line 1198) | def process_response(cls):
  class RedditGiftsController (line 1216) | class RedditGiftsController(GoldPaymentController):
    method process_response (line 1240) | def process_response(self):
  class GoldException (line 1266) | class GoldException(Exception): pass
  function validate_blob (line 1269) | def validate_blob(custom):
  function days_from_months (line 1322) | def days_from_months(months):
  function subtract_gold_days (line 1332) | def subtract_gold_days(user, days):
  function subtract_gold_creddits (line 1339) | def subtract_gold_creddits(user, num):
  function reverse_gold_purchase (line 1343) | def reverse_gold_purchase(transaction_id):
  function cancel_stripe_subscription (line 1383) | def cancel_stripe_subscription(customer_id):
  function subscr_pm (line 1391) | def subscr_pm(pennies, months, new_subscr=True):

FILE: r2/r2/controllers/listingcontroller.py
  class ListingController (line 69) | class ListingController(RedditController):
    method menus (line 112) | def menus(self):
    method can_send_referrer (line 117) | def can_send_referrer(self):
    method build_listing (line 123) | def build_listing(self, num, after, reverse, count, sr_detail=None, **...
    method content (line 160) | def content(self):
    method query (line 164) | def query(self):
    method builder (line 168) | def builder(self):
    method keep_fn (line 197) | def keep_fn(self):
    method prewrap_fn (line 218) | def prewrap_fn(self):
    method listing (line 221) | def listing(self):
    method title (line 243) | def title(self):
    method rightbox (line 247) | def rightbox(self):
    method GET_listing (line 255) | def GET_listing(self, **env):
  class SubredditListingController (line 272) | class SubredditListingController(ListingController):
    method _build_og_title (line 275) | def _build_og_title(self, max_length=256):
    method canonical_link (line 295) | def canonical_link(self):
    method _build_og_description (line 314) | def _build_og_description(self):
    method render_params (line 321) | def render_params(self):
  class ListingWithPromos (line 376) | class ListingWithPromos(SubredditListingController):
    method make_requested_ad (line 379) | def make_requested_ad(self, requested_ad):
    method make_single_ad (line 396) | def make_single_ad(self):
    method make_spotlight (line 402) | def make_spotlight(self):
    method content (line 454) | def content(self):
  class HotController (line 482) | class HotController(ListingWithPromos):
    method query (line 489) | def query(self):
    method trending_info (line 521) | def trending_info(cls):
    method content (line 537) | def content(self):
    method title (line 566) | def title(self):
    method GET_listing (line 571) | def GET_listing(self, **env):
  class NewController (line 575) | class NewController(ListingWithPromos):
    method keep_fn (line 582) | def keep_fn(self):
    method query (line 590) | def query(self):
    method POST_listing (line 594) | def POST_listing(self, **env):
    method GET_listing (line 600) | def GET_listing(self, **env):
  class RisingController (line 603) | class RisingController(NewController):
    method query (line 608) | def query(self):
  class BrowseController (line 617) | class BrowseController(ListingWithPromos):
    method keep_fn (line 622) | def keep_fn(self):
    method menus (line 637) | def menus(self):
    method query (line 640) | def query(self):
    method POST_listing (line 645) | def POST_listing(self, sort, t, **env):
    method GET_listing (line 655) | def GET_listing(self, sort, t, **env):
  class AdsController (line 673) | class AdsController(SubredditListingController):
    method infotext (line 679) | def infotext(self):
    method keep_fn (line 686) | def keep_fn(self):
    method query (line 696) | def query(self):
    method listing (line 702) | def listing(self):
    method GET_listing (line 706) | def GET_listing(self, *a, **kw):
  class RandomrisingController (line 713) | class RandomrisingController(ListingWithPromos):
    method query (line 718) | def query(self):
  class ByIDController (line 734) | class ByIDController(ListingController):
    method query (line 738) | def query(self):
    method GET_listing (line 745) | def GET_listing(self, links, **env):
  class UserController (line 757) | class UserController(ListingController):
    method menus (line 763) | def menus(self):
    method title (line 816) | def title(self):
    method keep_fn (line 833) | def keep_fn(self):
    method query (line 868) | def query(self):
    method GET_listing (line 925) | def GET_listing(self, where, vuser, sort, time, show, **env):
    method render_params (line 1028) | def render_params(self):
    method GET_about (line 1053) | def GET_about(self, vuser):
    method GET_saved_redirect (line 1061) | def GET_saved_redirect(self):
    method GET_rel_user_redirect (line 1075) | def GET_rel_user_redirect(self, rest=""):
    method GET_trophies (line 1084) | def GET_trophies(self, user):
  class MessageController (line 1091) | class MessageController(ListingController):
    method show_sidebar (line 1100) | def show_sidebar(self):
    method menus (line 1107) | def menus(self):
    method title (line 1128) | def title(self):
    method keep_fn (line 1131) | def keep_fn(self):
    method builder_wrapper (line 1175) | def builder_wrapper(thing):
    method builder (line 1188) | def builder(self):
    method _verify_inbox_count (line 1249) | def _verify_inbox_count(self, kept_msgs):
    method listing (line 1270) | def listing(self):
    method query (line 1290) | def query(self):
    method render_params (line 1333) | def render_params(self):
    method GET_listing (line 1359) | def GET_listing(self, where, mark, message, subwhere = None, **env):
    method GET_compose (line 1410) | def GET_compose(self, to, subject, message):
  class RedditsController (line 1457) | class RedditsController(ListingController):
    method title (line 1461) | def title(self):
    method keep_fn (line 1464) | def keep_fn(self):
    method query (line 1475) | def query(self):
    method render_params (line 1550) | def render_params(self):
    method GET_listing (line 1579) | def GET_listing(self, where, **env):
  class MyredditsController (line 1591) | class MyredditsController(ListingController):
    method menus (line 1596) | def menus(self):
    method title (line 1604) | def title(self):
    method builder_wrapper (line 1607) | def builder_wrapper(self, thing):
    method query (line 1615) | def query(self):
    method content (line 1636) | def content(self):
    method build_listing (line 1653) | def build_listing(self, after=None, **kwargs):
    method render_params (line 1660) | def render_params(self):
    method GET_listing (line 1682) | def GET_listing(self, where='subscriber', **env):
  class CommentsController (line 1700) | class CommentsController(SubredditListingController):
    method keep_fn (line 1703) | def keep_fn(self):
    method query (line 1727) | def query(self):
    method GET_listing (line 1731) | def GET_listing(self, **env):
  class UserListListingController (line 1737) | class UserListListingController(ListingController):
    method infotext (line 1744) | def infotext(self):
    method render_params (line 1752) | def render_params(self):
    method render_cls (line 1759) | def render_cls(self):
    method moderator_wrap (line 1764) | def moderator_wrap(self, rel, invited=False):
    method builder_wrapper (line 1770) | def builder_wrapper(self):
    method title (line 1789) | def title(self):
    method rel (line 1803) | def rel(self):
    method name (line 1808) | def name(self):
    method query (line 1822) | def query(self):
    method listing (line 1839) | def listing(self):
    method invited_mod_listing (line 1849) | def invited_mod_listing(self):
    method content (line 1861) | def content(self):
    method GET_user_prefs (line 1887) | def GET_user_prefs(self, where, **kw):
    method GET_listing (line 1926) | def GET_listing(self, where, user=None, **kw):
  class GildedController (line 2009) | class GildedController(SubredditListingController):
    method infotext (line 2014) | def infotext(self):
    method infotext_class (line 2030) | def infotext_class(self):
    method keep_fn (line 2033) | def keep_fn(self):
    method query (line 2038) | def query(self):
    method GET_listing (line 2045) | def GET_listing(self, **env):

FILE: r2/r2/controllers/login.py
  function handle_login (line 37) | def handle_login(
  function handle_register (line 87) | def handle_register(

FILE: r2/r2/controllers/mailgun.py
  function validate_mailgun_webhook (line 54) | def validate_mailgun_webhook(timestamp, token, signature):
  class MailgunWebhookController (line 80) | class MailgunWebhookController(RedditController):
    method POST_zendeskreply (line 84) | def POST_zendeskreply(self):

FILE: r2/r2/controllers/mediaembed.py
  class MediaembedController (line 39) | class MediaembedController(MinimalController):
    method GET_mediaembed (line 44) | def GET_mediaembed(self, link, credentials):
  class AdController (line 73) | class AdController(MinimalController):
    method GET_ad (line 74) | def GET_ad(self):

FILE: r2/r2/controllers/multi.py
  class MultiApiController (line 90) | class MultiApiController(RedditController):
    method on_validation_error (line 91) | def on_validation_error(self, error):
    method pre (line 94) | def pre(self):
    method _format_multi_list (line 98) | def _format_multi_list(self, multis, viewer, expand_srs):
    method GET_list_multis (line 110) | def GET_list_multis(self, user, expand_srs):
    method GET_my_multis (line 118) | def GET_my_multis(self, expand_srs):
    method _format_multi (line 123) | def _format_multi(self, multi, expand_sr_info=False):
    method GET_multi (line 137) | def GET_multi(self, multi, expand_srs):
    method _check_new_multi_path (line 141) | def _check_new_multi_path(self, path_info):
    method _add_multi_srs (line 147) | def _add_multi_srs(self, multi, sr_datas):
    method _write_multi_data (line 175) | def _write_multi_data(self, multi, data):
    method POST_multi (line 207) | def POST_multi(self, path_info, data):
    method PUT_multi (line 241) | def PUT_multi(self, path_info, data):
    method DELETE_multi (line 262) | def DELETE_multi(self, multi):
    method _copy_multi (line 266) | def _copy_multi(self, from_multi, to_path_info, rename=False):
    method POST_multi_copy (line 300) | def POST_multi_copy(self, from_multi, to_path_info, display_name):
    method POST_multi_rename (line 349) | def POST_multi_rename(self, from_multi, to_path_info, display_name):
    method _get_multi_subreddit (line 367) | def _get_multi_subreddit(self, multi, sr):
    method GET_multi_subreddit (line 382) | def GET_multi_subreddit(self, multi, sr):
    method PUT_multi_subreddit (line 395) | def PUT_multi_subreddit(self, multi, sr_name, data):
    method DELETE_multi_subreddit (line 418) | def DELETE_multi_subreddit(self, multi, sr):
    method _format_multi_description (line 423) | def _format_multi_description(self, multi):
    method GET_multi_description (line 436) | def GET_multi_description(self, multi):
    method PUT_multi_description (line 448) | def PUT_multi_description(self, multi, data):

FILE: r2/r2/controllers/newsletter.py
  class NewsletterController (line 27) | class NewsletterController(RedditController):
    method GET_newsletter (line 28) | def GET_newsletter(self):

FILE: r2/r2/controllers/oauth2.py
  function _update_redirect_uri (line 64) | def _update_redirect_uri(base_redirect_uri, params, as_fragment=False):
  function get_device_id (line 73) | def get_device_id(client):
  class OAuth2FrontendController (line 78) | class OAuth2FrontendController(RedditController):
    method check_for_bearer_token (line 79) | def check_for_bearer_token(self):
    method pre (line 82) | def pre(self):
    method _abort_oauth_error (line 86) | def _abort_oauth_error(self, error):
    method _check_redirect_uri (line 90) | def _check_redirect_uri(self, client, redirect_uri):
    method _check_response_type_and_scope (line 97) | def _check_response_type_and_scope(self, response_type, scope):
    method _check_client_type_and_duration (line 104) | def _check_client_type_and_duration(self, response_type, client, durat...
    method _error_response (line 114) | def _error_response(self, state, redirect_uri, as_fragment=False):
    method _check_employee_grants (line 128) | def _check_employee_grants(self, client, scope):
    method GET_authorize (line 154) | def GET_authorize(self, response_type, client, redirect_uri, scope, st...
    method POST_authorize (line 199) | def POST_authorize(self, authorize, client, redirect_uri, scope, state,
  class OAuth2AccessController (line 265) | class OAuth2AccessController(MinimalController):
    method pre (line 268) | def pre(self):
    method _get_client_auth (line 278) | def _get_client_auth(self):
    method OPTIONS_access_token (line 292) | def OPTIONS_access_token(self):
    method POST_access_token (line 323) | def POST_access_token(self, grant_type):
    method _check_for_errors (line 371) | def _check_for_errors(self):
    method _make_new_token_response (line 380) | def _make_new_token_response(cls, access_token, refresh_token=None):
    method _access_token_code (line 401) | def _access_token_code(self, code, redirect_uri):
    method _access_token_refresh (line 439) | def _access_token_refresh(self, refresh_token):
    method _access_token_password (line 467) | def _access_token_password(self, user, scope):
    method _access_token_client_credentials (line 506) | def _access_token_client_credentials(self, scope):
    method _access_token_extension_client_credentials (line 539) | def _access_token_extension_client_credentials(self, scope, device_id):
    method OPTIONS_revoke_token (line 572) | def OPTIONS_revoke_token(self):
    method POST_revoke_token (line 597) | def POST_revoke_token(self, token_id, token_hint):
  function require_oauth2_scope (line 650) | def require_oauth2_scope(*scopes):
  function allow_oauth2_access (line 657) | def allow_oauth2_access(fn):

FILE: r2/r2/controllers/oembed.py
  function _oembed_for (line 56) | def _oembed_for(thing, **embed_options):
  function _oembed_post (line 70) | def _oembed_post(thing, **embed_options):
  function _oembed_comment (line 117) | def _oembed_comment(thing, **embed_options):
  class OEmbedController (line 162) | class OEmbedController(MinimalController):
    method pre (line 163) | def pre(self):
    method GET_oembed (line 175) | def GET_oembed(self, url, parent, live, omitscript):

FILE: r2/r2/controllers/policies.py
  class PoliciesController (line 38) | class PoliciesController(RedditController):
    method GET_policy_page (line 40) | def GET_policy_page(self, page, requested_rev):
    method _number_sections (line 101) | def _number_sections(self, soup):
    method _linkify_headings (line 114) | def _linkify_headings(self, soup):

FILE: r2/r2/controllers/post.py
  class PostController (line 46) | class PostController(ApiController):
    method POST_unlogged_options (line 50) | def POST_unlogged_options(self, all_langs, pref_lang):
    method POST_options (line 59) | def POST_options(self, all_langs, **prefs):
    method GET_over18 (line 81) | def GET_over18(self):
    method GET_quarantine (line 90) | def GET_quarantine(self, dest):
    method POST_over18 (line 116) | def POST_over18(self, over18, dest):
    method POST_quarantine (line 138) | def POST_quarantine(self, sr, accept, dest):
    method POST_optout (line 155) | def POST_optout(self, msg_hash):
    method POST_optin (line 166) | def POST_optin(self, msg_hash):
    method POST_login (line 178) | def POST_login(self, dest, *a, **kw):
    method POST_reg (line 191) | def POST_reg(self, dest, *a, **kw):
    method GET_login (line 202) | def GET_login(self, *a, **kw):
    method POST_explore_settings (line 213) | def POST_explore_settings(self,

FILE: r2/r2/controllers/promotecontroller.py
  function _format_expires (line 162) | def _format_expires(expires):
  function _get_callback_hmac (line 166) | def _get_callback_hmac(username, key, expires):
  function _force_images (line 174) | def _force_images(link, thumbnail, mobile):
  function campaign_has_oversold_error (line 188) | def campaign_has_oversold_error(form, campaign):
  function has_oversold_error (line 204) | def has_oversold_error(form, campaign, start, end, total_budget_pennies,...
  function _key_to_dict (line 227) | def _key_to_dict(key, data=False):
  function _get_ads_keyspace (line 249) | def _get_ads_keyspace(thing):
  function _get_ads_images (line 253) | def _get_ads_images(thing, data=False, **kwargs):
  function _clear_ads_images (line 275) | def _clear_ads_images(thing):
  class PromoteController (line 284) | class PromoteController(RedditController):
    method GET_new_promo (line 286) | def GET_new_promo(self):
    method GET_edit_promo (line 298) | def GET_edit_promo(self, link):
    method GET_refund (line 310) | def GET_refund(self, link, campaign):
    method GET_pay (line 320) | def GET_pay(self, link, campaign):
  class SponsorController (line 345) | class SponsorController(PromoteController):
    method GET_report (line 352) | def GET_report(self, start, end, grouping, link_text=None, owner=None):
    method GET_promote_inventory (line 399) | def GET_promote_inventory(self, start, end, sr_name, collection_name):
    method GET_lookup_user (line 433) | def GET_lookup_user(self, id_user, email):
  class PromoteListingController (line 440) | class PromoteListingController(ListingController):
    method title (line 472) | def title(self):
    method title_text (line 476) | def title_text(self):
    method menus (line 480) | def menus(self):
    method builder_wrapper (line 500) | def builder_wrapper(self, thing):
    method keep_fn (line 507) | def keep_fn(self):
    method query (line 523) | def query(self):
    method GET_listing (line 540) | def GET_listing(self, sort="all", **env):
  class SponsorListingController (line 545) | class SponsorListingController(PromoteListingController):
    method title_text (line 555) | def title_text(self):
    method menus (line 559) | def menus(self):
    method _live_by_subreddit (line 607) | def _live_by_subreddit(cls, sr_names):
    method live_by_subreddit (line 611) | def live_by_subreddit(cls, sr):
    method get_house_link_names (line 616) | def get_house_link_names(cls):
    method keep_fn (line 626) | def keep_fn(self):
    method query (line 644) | def query(self):
    method listing (line 674) | def listing(self):
    method GET_listing (line 699) | def GET_listing(self, srname=None, include_managed=False,
  function allowed_location_and_target (line 718) | def allowed_location_and_target(location, target):
  class PromoteApiController (line 729) | class PromoteApiController(ApiController):
    method GET_check_inventory (line 737) | def GET_check_inventory(self, responder, sr, collection, location, start,
    method POST_freebie (line 759) | def POST_freebie(self, form, jquery, link, campaign):
    method POST_promote_note (line 775) | def POST_promote_note(self, form, jquery, link, note):
    method POST_review_fraud (line 787) | def POST_review_fraud(self, form, jquery, thing, is_fraud):
    method POST_promote (line 800) | def POST_promote(self, thing):
    method POST_unpromote (line 808) | def POST_unpromote(self, thing, reason):
    method POST_refund_campaign (line 816) | def POST_refund_campaign(self, form, jquery, link, campaign):
    method POST_create_promo (line 869) | def POST_create_promo(self, form, jquery, username, title, url,
    method POST_edit_promo (line 915) | def POST_edit_promo(self, form, jquery, username, title, url,
    method _edit_promo (line 935) | def _edit_promo(self, form, jquery, username, title, url,
    method _lowest_max_cpm_bid_dollars (line 1163) | def _lowest_max_cpm_bid_dollars(self, total_budget_dollars, bid_dollars,
    method POST_edit_campaign (line 1199) | def POST_edit_campaign(self, form, jquery, is_auction, link, campaign_...
    method POST_delete_campaign (line 1486) | def POST_delete_campaign(self, form, jquery, l, campaign):
    method POST_toggle_pause_campaign (line 1498) | def POST_toggle_pause_campaign(self, form, jquery, link, campaign,
    method POST_terminate_campaign (line 1515) | def POST_terminate_campaign(self, form, jquery, link, campaign):
    method POST_update_pay (line 1537) | def POST_update_pay(self, form, jquery, link, campaign, customer_id, p...
    method POST_ad_s3_params (line 1632) | def POST_ad_s3_params(self, responder, link, kind, filepath, ajax):
    method GET_ad_s3_callback (line 1675) | def GET_ad_s3_callback(self, expires, signature, callback, key):

FILE: r2/r2/controllers/reddit_base.py
  class UnloggedUser (line 143) | class UnloggedUser(FakeAccount):
    method __init__ (line 151) | def __init__(self, browser_langs, *a, **kw):
    method name (line 161) | def name(self):
    method _decode_json (line 164) | def _decode_json(self, json_blob):
    method _from_cookie (line 176) | def _from_cookie(self):
    method _to_cookie (line 196) | def _to_cookie(self, data):
    method _subscribe (line 202) | def _subscribe(self, sr):
    method _unsubscribe (line 205) | def _unsubscribe(self, sr):
    method _commit (line 208) | def _commit(self):
  function read_user_cookie (line 214) | def read_user_cookie(name):
  function set_user_cookie (line 222) | def set_user_cookie(name, val, **kwargs):
  function set_recent_clicks (line 228) | def set_recent_clicks():
  function delete_obsolete_cookies (line 254) | def delete_obsolete_cookies():
  function over18 (line 259) | def over18():
  function set_over18_cookie (line 271) | def set_over18_cookie():
  function delete_over18_cookie (line 275) | def delete_over18_cookie():
  function set_obey_over18 (line 279) | def set_obey_over18():
  function set_subreddit (line 284) | def set_subreddit():
  function set_multireddit (line 374) | def set_multireddit():
  function set_content_type (line 439) | def set_content_type():
  function get_browser_langs (line 497) | def get_browser_langs():
  function set_iface_lang (line 518) | def set_iface_lang():
  function set_colors (line 539) | def set_colors():
  function ratelimit_agent (line 549) | def ratelimit_agent(agent, limit=10, slice_size=10):
  function ratelimit_agents (line 565) | def ratelimit_agents():
  function ratelimit_throttled (line 585) | def ratelimit_throttled():
  function paginated_listing (line 591) | def paginated_listing(default_page_size=25, max_page_size=100, backend='...
  function base_listing (line 637) | def base_listing(fn):
  function is_trusted_origin (line 640) | def is_trusted_origin(origin):
  function cross_domain (line 648) | def cross_domain(origin_check=is_trusted_origin, **options):
  function make_url_https (line 677) | def make_url_https(url):
  function generate_modhash (line 686) | def generate_modhash():
  function enforce_https (line 700) | def enforce_https():
  function require_https (line 752) | def require_https():
  function require_domain (line 757) | def require_domain(required_domain):
  function disable_subreddit_css (line 762) | def disable_subreddit_css():
  function request_timer_name (line 772) | def request_timer_name(action):
  function flatten_response (line 776) | def flatten_response(content):
  function abort_with_error (line 783) | def abort_with_error(error, code=None):
  class MinimalController (line 795) | class MinimalController(BaseController):
    method run_sitewide_ratelimits (line 800) | def run_sitewide_ratelimits(self):
    method pre (line 886) | def pre(self):
    method post (line 952) | def post(self):
    method on_validation_error (line 1043) | def on_validation_error(self, error):
    method abort404 (line 1049) | def abort404(self):
    method abort403 (line 1052) | def abort403(self):
    method check_cors (line 1062) | def check_cors(self):
    method OPTIONS (line 1096) | def OPTIONS(self):
    method update_qstring (line 1100) | def update_qstring(self, dict):
    method api_wrapper (line 1105) | def api_wrapper(self, kw):
    method should_update_last_visit (line 1110) | def should_update_last_visit(self):
  class OAuth2ResourceController (line 1123) | class OAuth2ResourceController(MinimalController):
    method authenticate_with_token (line 1126) | def authenticate_with_token(self):
    method check_for_bearer_token (line 1162) | def check_for_bearer_token(self):
    method _auth_error (line 1166) | def _auth_error(self, code, error):
    method _get_bearer_token (line 1169) | def _get_bearer_token(self, strict=True):
    method set_up_user_context (line 1183) | def set_up_user_context(self):
  class OAuth2OnlyController (line 1191) | class OAuth2OnlyController(OAuth2ResourceController):
    method pre (line 1198) | def pre(self):
    method on_validation_error (line 1205) | def on_validation_error(self, error):
  class RedditController (line 1209) | class RedditController(OAuth2ResourceController):
    method login (line 1212) | def login(user, rem=False):
    method logout (line 1224) | def logout():
    method enable_admin_mode (line 1229) | def enable_admin_mode(user, first_login=None):
    method remember_otp (line 1239) | def remember_otp(user):
    method disable_admin_mode (line 1249) | def disable_admin_mode(user):
    method pre (line 1252) | def pre(self):
    method post (line 1460) | def post(self):
    method _embed_html_timing_data (line 1479) | def _embed_html_timing_data(self):
    method search_fail (line 1502) | def search_fail(self, exception):

FILE: r2/r2/controllers/redirect.py
  class RedirectController (line 32) | class RedirectController(BaseController):
    method pre (line 33) | def pre(self, *k, **kw):
    method GET_redirect (line 37) | def GET_redirect(self, dest):
    method GET_user_redirect (line 40) | def GET_user_redirect(self, username, rest=None):
    method GET_timereddit_redirect (line 51) | def GET_timereddit_redirect(self, timereddit, rest=None):

FILE: r2/r2/controllers/robots.py
  class RobotsController (line 31) | class RobotsController(MinimalController):
    method pre (line 32) | def pre(self):
    method post (line 35) | def post(self):
    method on_crawlable_domain (line 38) | def on_crawlable_domain(self):
    method GET_robots (line 56) | def GET_robots(self):
    method GET_crossdomain (line 63) | def GET_crossdomain(self):

FILE: r2/r2/controllers/toolbar.py
  function demangle_url (line 44) | def demangle_url(path):
  function force_html (line 67) | def force_html():
  class ToolbarController (line 80) | class ToolbarController(RedditController):
    method GET_goto (line 86) | def GET_goto(self, link1, link2):
    method GET_s (line 94) | def GET_s(self, urloid):
    method GET_redirect (line 120) | def GET_redirect(self):

FILE: r2/r2/controllers/web.py
  class WebLogController (line 50) | class WebLogController(RedditController):
    method POST_message (line 65) | def POST_message(self, level, logs):
    method OPTIONS_report_cache_poisoning (line 97) | def OPTIONS_report_cache_poisoning(self):
    method POST_report_cache_poisoning (line 133) | def POST_report_cache_poisoning(

FILE: r2/r2/controllers/wiki.py
  class WikiController (line 115) | class WikiController(RedditController):
    method GET_wiki_page (line 126) | def GET_wiki_page(self, pv, page_name):
    method GET_wiki_revisions (line 190) | def GET_wiki_revisions(self, num, after, reverse, count, page):
    method GET_wiki_create (line 204) | def GET_wiki_create(self, wp, page):
    method GET_wiki_revise (line 233) | def GET_wiki_revise(self, wp, page, message=None, **kw):
    method GET_wiki_recent (line 251) | def GET_wiki_recent(self, num, after, reverse, count):
    method GET_wiki_listing (line 265) | def GET_wiki_listing(self):
    method GET_wiki_redirect (line 272) | def GET_wiki_redirect(self, page='index'):
    method GET_wiki_discussions (line 279) | def GET_wiki_discussions(self, page, num, after, reverse, count):
    method GET_wiki_settings (line 291) | def GET_wiki_settings(self, page):
    method POST_wiki_settings (line 313) | def POST_wiki_settings(self, page, permlevel, listed):
    method on_validation_error (line 342) | def on_validation_error(self, error):
    method handle_error (line 347) | def handle_error(self, code, reason=None, **data):
    method pre (line 350) | def pre(self):
    method GET_faq (line 374) | def GET_faq(self):
  class WikiApiController (line 380) | class WikiApiController(WikiController):
    method POST_wiki_edit (line 388) | def POST_wiki_edit(self, pageandprevious, content, page_name, reason):
    method POST_wiki_allow_editor (line 471) | def POST_wiki_allow_editor(self, act, page, user):
    method POST_wiki_revision_delete (line 493) | def POST_wiki_revision_delete(self, pv, deleted):
    method POST_wiki_revision_hide (line 508) | def POST_wiki_revision_hide(self, pv):
    method POST_wiki_revision_revert (line 523) | def POST_wiki_revision_revert(self, pv):
    method pre (line 550) | def pre(self):

FILE: r2/r2/lib/amqp.py
  function initialize (line 54) | def initialize(app_globals):
  class Config (line 63) | class Config(object):
    method __init__ (line 64) | def __init__(self, g):
  class Worker (line 77) | class Worker:
    method __init__ (line 78) | def __init__(self):
    method _handle (line 84) | def _handle(self):
    method do (line 96) | def do(self, fn, *a, **kw):
    method join (line 100) | def join(self):
  class ConnectionManager (line 104) | class ConnectionManager(local):
    method __init__ (line 109) | def __init__(self):
    method get_connection (line 114) | def get_connection(self):
    method get_channel (line 138) | def get_channel(self, reconnect = False):
    method init_queue (line 158) | def init_queue(self):
  function _add_item (line 181) | def _add_item(routing_key, body, message_id = None,
  function add_item (line 220) | def add_item(routing_key, body, message_id=None,
  function add_kw (line 232) | def add_kw(routing_key, **kw):
  function consume_items (line 235) | def consume_items(queue, callback, verbose=True):
  function handle_items (line 288) | def handle_items(queue, callback, ack=True, limit=1, min_size=0,
  function empty_queue (line 357) | def empty_queue(queue):
  function black_hole (line 363) | def black_hole(queue):
  function dedup_queue (line 370) | def dedup_queue(queue, rk = None, limit=None,

FILE: r2/r2/lib/app_globals.py
  function extract_live_config (line 100) | def extract_live_config(config, plugins):
  function _decode_secrets (line 117) | def _decode_secrets(secrets):
  function extract_secrets (line 121) | def extract_secrets(config):
  function fetch_secrets (line 129) | def fetch_secrets(zk_client):
  class PermissionFilteredEmployeeList (line 142) | class PermissionFilteredEmployeeList(object):
    method __init__ (line 143) | def __init__(self, config, type):
    method __iter__ (line 147) | def __iter__(self):
    method __getitem__ (line 152) | def __getitem__(self, key):
    method __contains__ (line 155) | def __contains__(self, item):
    method __repr__ (line 161) | def __repr__(self):
  function on_app_shutdown (line 166) | def on_app_shutdown(arbiter, worker):
  class Globals (line 171) | class Globals(object):
    method __init__ (line 429) | def __init__(self, config, global_conf, app_conf, paths, **extra):
    method __getattr__ (line 509) | def __getattr__(self, name):
    method setup (line 515) | def setup(self):
    method setup_complete (line 962) | def setup_complete(self):
    method record_repo_version (line 979) | def record_repo_version(self, repo_name, git_dir):
    method load_db_params (line 1000) | def load_db_params(self):
    method __del__ (line 1075) | def __del__(self):
    method search (line 1083) | def search(self):
    method search_syntaxes (line 1095) | def search_syntaxes(self):

FILE: r2/r2/lib/authorize/api.py
  class AuthorizeNetException (line 74) | class AuthorizeNetException(Exception):
    method __init__ (line 75) | def __init__(self, msg, code=None):
  class TransactionError (line 87) | class TransactionError(Exception):
    method __init__ (line 88) | def __init__(self, message):
  class DuplicateTransactionError (line 92) | class DuplicateTransactionError(TransactionError):
    method __init__ (line 93) | def __init__(self, transaction_id):
  class AuthorizationHoldNotFound (line 100) | class AuthorizationHoldNotFound(Exception): pass
  class SimpleXMLObject (line 107) | class SimpleXMLObject(object):
    method __init__ (line 114) | def __init__(self, **kw):
    method simple_tag (line 121) | def simple_tag(name, content, **attrs):
    method toXML (line 128) | def toXML(self):
    method fromXML (line 150) | def fromXML(cls, data):
    method __repr__ (line 159) | def __repr__(self):
    method _name (line 164) | def _name(self):
    method _wrapper (line 168) | def _wrapper(self, content):
  class Auth (line 172) | class Auth(SimpleXMLObject):
  class Address (line 177) | class Address(SimpleXMLObject):
    method __init__ (line 183) | def __init__(self, **kw):
  class CreditCard (line 191) | class CreditCard(SimpleXMLObject):
  class Profile (line 195) | class Profile(SimpleXMLObject):
    method __init__ (line 199) | def __init__(self, description, merchantCustomerId, customerProfileId,
  class PaymentProfile (line 212) | class PaymentProfile(SimpleXMLObject):
    method __init__ (line 214) | def __init__(self, billTo, card, customerPaymentProfileId=None,
    method fromXML (line 225) | def fromXML(cls, res):
  class Order (line 233) | class Order(SimpleXMLObject):
  class Transaction (line 237) | class Transaction(SimpleXMLObject):
    method __init__ (line 241) | def __init__(self, amount, customerProfileId, customerPaymentProfileId,
    method _wrapper (line 248) | def _wrapper(self, content):
  class ProfileTransAuthOnly (line 254) | class ProfileTransAuthOnly(Transaction): pass
  class ProfileTransPriorAuthCapture (line 259) | class ProfileTransPriorAuthCapture(Transaction): pass
  class ProfileTransRefund (line 264) | class ProfileTransRefund(Transaction): pass
  class ProfileTransVoid (line 269) | class ProfileTransVoid(Transaction): pass
  class AuthorizeNetRequest (line 273) | class AuthorizeNetRequest(SimpleXMLObject):
    method merchantAuthentication (line 277) | def merchantAuthentication(self):
    method _wrapper (line 281) | def _wrapper(self, content):
    method make_request (line 286) | def make_request(self):
    method is_error_code (line 296) | def is_error_code(self, res, code):
    method _autoclose_handler (line 301) | def _autoclose_handler(self, m):
    method handle_response (line 304) | def handle_response(self, res):
    method process_response (line 314) | def process_response(self, res):
    method process_error (line 317) | def process_error(self, res):
  class CreateCustomerProfileRequest (line 323) | class CreateCustomerProfileRequest(AuthorizeNetRequest):
    method __init__ (line 326) | def __init__(self, profile, validationMode=None):
    method process_response (line 330) | def process_response(self, res):
    method process_error (line 334) | def process_error(self, res):
  class CreateCustomerPaymentProfileRequest (line 350) | class CreateCustomerPaymentProfileRequest(AuthorizeNetRequest):
    method __init__ (line 354) | def __init__(self, customerProfileId, paymentProfile, validationMode=N...
    method process_response (line 359) | def process_response(self, res):
    method process_error (line 363) | def process_error(self, res):
  class GetCustomerProfileRequest (line 368) | class GetCustomerProfileRequest(AuthorizeNetRequest):
    method __init__ (line 371) | def __init__(self, customerProfileId):
    method process_response (line 375) | def process_response(self, res):
    method process_error (line 401) | def process_error(self, res):
  class DeleteCustomerPaymentProfileRequest (line 408) | class DeleteCustomerPaymentProfileRequest(AuthorizeNetRequest):
    method __init__ (line 412) | def __init__(self, customerProfileId, customerPaymentProfileId):
    method process_response (line 417) | def process_response(self, res):
    method process_error (line 420) | def process_error(self, res):
  class UpdateCustomerPaymentProfileRequest (line 428) | class UpdateCustomerPaymentProfileRequest(AuthorizeNetRequest):
    method __init__ (line 432) | def __init__(self, customerProfileId, paymentProfile, validationMode=N...
    method process_response (line 437) | def process_response(self, res):
    method process_error (line 440) | def process_error(self, res):
  class CreateCustomerProfileTransactionRequest (line 445) | class CreateCustomerProfileTransactionRequest(AuthorizeNetRequest):
    method __init__ (line 482) | def __init__(self, **kw):
    method extraOptions (line 487) | def extraOptions(self):
    method process_response (line 491) | def process_response(self, res):
    method process_error (line 494) | def process_error(self, res):
    method package_response (line 497) | def package_response(self, res):
  class GetSettledBatchListRequest (line 508) | class GetSettledBatchListRequest(AuthorizeNetRequest):
    method __init__ (line 512) | def __init__(self, start_date, end_date, **kw):
    method process_response (line 519) | def process_response(self, res):
    method process_error (line 522) | def process_error(self, res):
  function create_customer_profile (line 527) | def create_customer_profile(merchant_customer_id, description):
  function get_customer_profile (line 545) | def get_customer_profile(customer_id):
  function create_payment_profile (line 556) | def create_payment_profile(customer_id, address, credit_card, validate=F...
  function update_payment_profile (line 570) | def update_payment_profile(customer_id, payment_profile_id, address,
  function delete_payment_profile (line 590) | def delete_payment_profile(customer_id, payment_profile_id):
  function create_authorization_hold (line 604) | def create_authorization_hold(customer_id, payment_profile_id, amount, i...
  function capture_authorization_hold (line 634) | def capture_authorization_hold(customer_id, payment_profile_id, amount,
  function void_authorization_hold (line 656) | def void_authorization_hold(customer_id, payment_profile_id, transaction...
  function refund_transaction (line 674) | def refund_transaction(customer_id, payment_profile_id, amount, transact...

FILE: r2/r2/lib/authorize/interaction.py
  function get_or_create_customer_profile (line 42) | def get_or_create_customer_profile(user):
  function add_payment_method (line 60) | def add_payment_method(user, address, credit_card, validate=False):
  function update_payment_method (line 70) | def update_payment_method(user, payment_method_id, address, credit_card,
  function delete_payment_method (line 79) | def delete_payment_method(user, payment_method_id):
  function add_or_update_payment_method (line 87) | def add_or_update_payment_method(user, address, credit_card, pay_id=None):
  function is_charged_transaction (line 96) | def is_charged_transaction(trans_id, campaign):
  function auth_freebie_transaction (line 110) | def auth_freebie_transaction(amount, user, link, campaign_id):
  function auth_transaction (line 128) | def auth_transaction(amount, user, payment_method_id, link, campaign_id):
  function charge_transaction (line 156) | def charge_transaction(user, transaction_id, campaign_id):
  function void_transaction (line 186) | def void_transaction(user, transaction_id, campaign_id):
  function refund_transaction (line 204) | def refund_transaction(user, transaction_id, campaign_id, amount):

FILE: r2/r2/lib/automoderator.py
  function replace_placeholders (line 94) | def replace_placeholders(string, data, matches):
  class AutoModeratorSyntaxError (line 176) | class AutoModeratorSyntaxError(ValueError):
    method __init__ (line 177) | def __init__(self, message, yaml):
  class AutoModeratorRuleTypeError (line 184) | class AutoModeratorRuleTypeError(AutoModeratorSyntaxError):
  class Ruleset (line 192) | class Ruleset(object):
    method __init__ (line 194) | def __init__(self, yaml_text="", timer=None):
    method __iter__ (line 293) | def __iter__(self):
    method __len__ (line 298) | def __len__(self):
    method removal_rules (line 302) | def removal_rules(self):
    method nonremoval_rules (line 309) | def nonremoval_rules(self):
    method apply_to_item (line 315) | def apply_to_item(self, item):
  class RuleComponent (line 357) | class RuleComponent(object):
    method __init__ (line 360) | def __init__(self, valid_types=None, valid_values=None,
    method validate (line 384) | def validate(self, value):
  class RuleTarget (line 399) | class RuleTarget(object):
    method __init__ (line 585) | def __init__(self, target_type, values, parent, approve_banned=True):
    method set_values (line 610) | def set_values(self, values):
    method parse_match_fields_key (line 673) | def parse_match_fields_key(self, key):
    method get_match_patterns (line 722) | def get_match_patterns(self, values):
    method needs_media_data (line 779) | def needs_media_data(self):
    method check_item (line 795) | def check_item(self, item, data):
    method check_nonpattern_conditions (line 809) | def check_nonpattern_conditions(self, item, data):
    method check_account_thresholds (line 868) | def check_account_thresholds(self, account, data):
    method check_match_patterns (line 929) | def check_match_patterns(self, item, data):
    method perform_actions (line 967) | def perform_actions(self, item, data):
    method get_field_value_from_item (line 1123) | def get_field_value_from_item(self, item, data, field):
  class Rule (line 1176) | class Rule(object):
    method __init__ (line 1207) | def __init__(self, values, yaml_source=None):
    method is_removal_rule (line 1284) | def is_removal_rule(self):
    method is_inapplicable_to_mods (line 1289) | def is_inapplicable_to_mods(self):
    method is_unrepeatable (line 1300) | def is_unrepeatable(self):
    method needs_media_data (line 1313) | def needs_media_data(self):
    method matches (line 1326) | def matches(self):
    method has_any_checks (line 1329) | def has_any_checks(self, targets_only=False):
    method has_any_actions (line 1339) | def has_any_actions(self, targets_only=False):
    method get_target_item (line 1349) | def get_target_item(self, item, data, key):
    method item_is_correct_type (line 1358) | def item_is_correct_type(self, item):
    method should_check_item (line 1370) | def should_check_item(self, item, data):
    method check_item (line 1404) | def check_item(self, item, data):
    method perform_actions (line 1418) | def perform_actions(self, item, data):
    method build_message (line 1487) | def build_message(self, text, item, data, disclaimer=False, permalink=...
  function run (line 1501) | def run():

FILE: r2/r2/lib/base.py
  function is_local_address (line 47) | def is_local_address(ip):
  function abort (line 51) | def abort(code_or_exception=None, detail="", headers=None, comment=None,
  class BaseController (line 71) | class BaseController(WSGIController):
    method __before__ (line 72) | def __before__(self):
    method __after__ (line 131) | def __after__(self):
    method __call__ (line 134) | def __call__(self, environ, start_response):
    method pre (line 158) | def pre(self): pass
    method post (line 159) | def post(self): pass
    method fix_cookie_header (line 161) | def fix_cookie_header(self):
    method _get_action_handler (line 185) | def _get_action_handler(self, name=None, method=None):
    method format_output_url (line 192) | def format_output_url(cls, url, **kw):
    method intermediate_redirect (line 222) | def intermediate_redirect(cls, form_path, sr_path=True, fullpath=None):
    method redirect (line 239) | def redirect(cls, dest, code=302, preserve_extension=True):
  class EmbedHandler (line 250) | class EmbedHandler(urllib2.BaseHandler, urllib2.HTTPHandler,
    method http_redirect (line 253) | def http_redirect(self, req, fp, code, msg, hdrs):
  function proxyurl (line 267) | def proxyurl(url):

FILE: r2/r2/lib/baseplate_integration.py
  function make_server_span (line 39) | def make_server_span(span_name):
  function finish_server_span (line 44) | def finish_server_span():
  function with_server_span (line 48) | def with_server_span(name):
  class R2BaseplateObserver (line 79) | class R2BaseplateObserver(BaseplateObserver):
    method on_server_span_created (line 80) | def on_server_span_created(self, context, server_span):
  class R2ServerSpanObserver (line 85) | class R2ServerSpanObserver(ServerSpanObserver):
    method on_child_span_created (line 86) | def on_child_span_created(self, span):
  class R2SpanObserver (line 91) | class R2SpanObserver(SpanObserver):
    method __init__ (line 92) | def __init__(self, span_name):
    method on_start (line 96) | def on_start(self):
    method on_finish (line 99) | def on_finish(self, exc_info):

FILE: r2/r2/lib/butler.py
  function notify_mention (line 33) | def notify_mention(user, thing):
  function remove_mention_notification (line 46) | def remove_mention_notification(mention):
  function readd_mention_notification (line 54) | def readd_mention_notification(mention):
  function monitor_mentions (line 64) | def monitor_mentions(comment):
  function run (line 123) | def run():

FILE: r2/r2/lib/c/filters.c
  function PyObject (line 29) | PyObject *unicode_arg(PyObject *args) {
  function PyObject (line 42) | static PyObject *
  function PyObject (line 100) | static PyObject *
  function PyObject (line 151) | static PyObject *
  function print_unicode (line 206) | void print_unicode(Py_UNICODE *c, int len) {
  function whitespace (line 224) | int whitespace(char c) {
  function PyObject (line 229) | static PyObject *
  function Py_UNICODE (line 311) | Py_UNICODE *to_unicode(const char *c, int len) {
  function PyMODINIT_FUNC (line 327) | PyMODINIT_FUNC

FILE: r2/r2/lib/cache.py
  class NoneResult (line 47) | class NoneResult(object): pass
  class CacheUtils (line 49) | class CacheUtils(object):
    method incr_multi (line 54) | def incr_multi(self, keys, delta=1, prefix=''):
    method add_multi (line 61) | def add_multi(self, keys, prefix='', time=0):
    method get_multi (line 65) | def get_multi(self, keys, prefix='', **kw):
  class CMemcache (line 69) | class CMemcache(CacheUtils):
    method __init__ (line 70) | def __init__(self,
    method get (line 101) | def get(self, key, default = None):
    method get_multi (line 108) | def get_multi(self, keys, prefix = ''):
    method set (line 121) | def set(self, key, val, time=0):
    method set_multi (line 130) | def set_multi(self, keys, prefix='', time=0):
    method add_multi (line 139) | def add_multi(self, keys, prefix='', time=0):
    method incr_multi (line 148) | def incr_multi(self, keys, prefix='', delta=1):
    method append (line 153) | def append(self, key, val, time=0):
    method incr (line 161) | def incr(self, key, delta=1, time=0):
    method add (line 166) | def add(self, key, val, time=0):
    method delete (line 177) | def delete(self, key, time=0):
    method delete_multi (line 181) | def delete_multi(self, keys, prefix=''):
    method __repr__ (line 186) | def __repr__(self):
  class Mcrouter (line 191) | class Mcrouter(CMemcache):
    method set (line 234) | def set(self, key, val, time=0):
  class HardCache (line 245) | class HardCache(CacheUtils):
    method __init__ (line 249) | def __init__(self, gc):
    method _split_key (line 252) | def _split_key(self, key):
    method set (line 260) | def set(self, key, val, time=0):
    method simple_get_multi (line 268) | def simple_get_multi(self, keys):
    method set_multi (line 284) | def set_multi(self, keys, prefix='', time=0):
    method get (line 289) | def get(self, key, default=None):
    method delete (line 295) | def delete(self, key, time=0):
    method add (line 301) | def add(self, key, value, time=0):
    method incr (line 305) | def incr(self, key, delta=1, time=0):
  class LocalCache (line 310) | class LocalCache(dict, CacheUtils):
    method __init__ (line 311) | def __init__(self, *a, **kw):
    method _check_key (line 314) | def _check_key(self, key):
    method get (line 320) | def get(self, key, default=None):
    method simple_get_multi (line 325) | def simple_get_multi(self, keys):
    method set (line 334) | def set(self, key, val, time = 0):
    method set_multi (line 339) | def set_multi(self, keys, prefix='', time=0):
    method add (line 343) | def add(self, key, val, time = 0):
    method delete (line 349) | def delete(self, key):
    method delete_multi (line 353) | def delete_multi(self, keys):
    method incr (line 358) | def incr(self, key, delta=1, time=0):
    method decr (line 362) | def decr(self, key, amt=1):
    method append (line 366) | def append(self, key, val, time = 0):
    method prepend (line 370) | def prepend(self, key, val, time = 0):
    method replace (line 374) | def replace(self, key, val, time = 0):
    method flush_all (line 378) | def flush_all(self):
    method reset (line 381) | def reset(self):
    method __repr__ (line 384) | def __repr__(self):
  class TransitionalCache (line 388) | class TransitionalCache(CacheUtils):
    method __init__ (line 411) | def __init__(
    method stats (line 420) | def stats(self):
    method read_chain (line 427) | def read_chain(self):
    method caches (line 434) | def caches(self):
    method transform_memcache_key (line 440) | def transform_memcache_key(self, args, kwargs):
    method make_get_fn (line 516) | def make_get_fn(fn_name):
    method make_set_fn (line 529) | def make_set_fn(fn_name):
  function cache_timer_decorator (line 558) | def cache_timer_decorator(fn_name):
  class CacheChain (line 588) | class CacheChain(CacheUtils, local):
    method __init__ (line 589) | def __init__(self, caches, cache_negative_results=False):
    method make_set_fn (line 594) | def make_set_fn(fn_name):
    method get (line 625) | def get(self, key, default = None, allow_local = True, stale=None):
    method get_multi (line 664) | def get_multi(self, keys, prefix='', allow_local = True, **kw):
    method simple_get_multi (line 669) | def simple_get_multi(self, keys, allow_local = True, stale=None,
    method __repr__ (line 725) | def __repr__(self):
    method debug (line 729) | def debug(self, key):
    method reset (line 735) | def reset(self):
  class MemcacheChain (line 739) | class MemcacheChain(CacheChain):
  class HardcacheChain (line 742) | class HardcacheChain(CacheChain):
    method add (line 743) | def add(self, key, val, time=0):
    method accrue (line 754) | def accrue(self, key, time=0, delta=1):
    method backend (line 772) | def backend(self):
  class StaleCacheChain (line 776) | class StaleCacheChain(CacheChain):
    method __init__ (line 782) | def __init__(self, localcache, stalecache, realcache):
    method get (line 791) | def get(self, key, default=None, stale = False, **kw):
    method simple_get_multi (line 825) | def simple_get_multi(self, keys, stale=False, stat_subname=None, **kw):
    method _getstale (line 867) | def _getstale(self, keys):
    method reset (line 872) | def reset(self):
    method __repr__ (line 880) | def __repr__(self):
  class Permacache (line 888) | class Permacache(object):
    method __init__ (line 898) | def __init__(self, cache_chain, column_family, lock_factory):
    method _setup_column_family (line 904) | def _setup_column_family(cls, column_family_name, client):
    method _backend_get (line 910) | def _backend_get(self, keys):
    method _backend_set (line 925) | def _backend_set(self, key, val):
    method _backend_set_multi (line 930) | def _backend_set_multi(self, keys, prefix=''):
    method _backend_delete (line 939) | def _backend_delete(self, key):
    method get (line 942) | def get(self, key, default=None, allow_local=True, stale=False):
    method set (line 952) | def set(self, key, val):
    method set_multi (line 956) | def set_multi(self, keys, prefix='', time=None):
    method pessimistically_set (line 961) | def pessimistically_set(self, key, value):
    method get_multi (line 970) | def get_multi(self, keys, prefix='', allow_local=True, stale=False):
    method simple_get_multi (line 975) | def simple_get_multi(self, keys, allow_local=True, stale=False):
    method delete (line 985) | def delete(self, key):
    method mutate (line 989) | def mutate(self, key, mutation_fn, default=None, willread=True):
    method __repr__ (line 1010) | def __repr__(self):
  function test_cache (line 1015) | def test_cache(cache, prefix=''):
  function test_multi (line 1061) | def test_multi(cache):
  class SelfEmptyingCache (line 1083) | class SelfEmptyingCache(LocalCache):
    method __init__ (line 1084) | def __init__(self, max_size=10*1000):
    method maybe_reset (line 1087) | def maybe_reset(self):
    method set (line 1091) | def set(self, key, val, time=0):
    method add (line 1095) | def add(self, key, val, time=0):
  function _make_hashable (line 1100) | def _make_hashable(s):
  function make_key_id (line 1114) | def make_key_id(*a, **kw):
  function test_stale (line 1121) | def test_stale():

FILE: r2/r2/lib/cache_poisoning.py
  function make_poisoning_report_mac (line 26) | def make_poisoning_report_mac(
  function cache_headers_valid (line 51) | def cache_headers_valid(policy_name, headers):

FILE: r2/r2/lib/captcha.py
  class RandCaptcha (line 36) | class RandCaptcha(ImageCaptcha):
    method getLayers (line 40) | def getLayers(self, solution="blah"):
  function get_iden (line 49) | def get_iden():
  function make_solution (line 52) | def make_solution():
  function get_image (line 55) | def get_image(iden):
  function valid_solution (line 64) | def valid_solution(iden, solution):

FILE: r2/r2/lib/comment_tree.py
  function write_comment_scores (line 37) | def write_comment_scores(link, comments):
  function add_comments (line 43) | def add_comments(comments):
  function calculate_comment_scores (line 73) | def calculate_comment_scores(link, sort, comments):
  function _calculate_qa_comment_scores (line 89) | def _calculate_qa_comment_scores(link, cid_tree, comments):
  function get_comment_scores (line 124) | def get_comment_scores(link, sort, comment_ids, timer):
  function messages_key (line 191) | def messages_key(user_id):
  function messages_lock_key (line 194) | def messages_lock_key(user_id):
  function add_message (line 197) | def add_message(message, update_recipient=True, update_modmail=True,
  function _add_message_nolock (line 215) | def _add_message_nolock(key, message):
  function add_message_nolock (line 258) | def add_message_nolock(user_id, message):
  function _conversation (line 261) | def _conversation(trees, parent):
  function conversation (line 280) | def conversation(user, parent):
  function user_messages (line 285) | def user_messages(user, update = False):
  function _load_messages (line 294) | def _load_messages(mlist):
  function user_messages_nocache (line 303) | def user_messages_nocache(user):
  function sr_messages_key (line 313) | def sr_messages_key(sr_id):
  function sr_messages_lock_key (line 316) | def sr_messages_lock_key(sr_id):
  function subreddit_messages (line 320) | def subreddit_messages(sr, update = False):
  function moderator_messages (line 328) | def moderator_messages(sr_ids):
  function subreddit_messages_nocache (line 348) | def subreddit_messages_nocache(sr):
  function add_sr_message_nolock (line 358) | def add_sr_message_nolock(sr_id, message):
  function sr_conversation (line 361) | def sr_conversation(sr, parent):
  function compute_message_trees (line 366) | def compute_message_trees(messages):
  function tree_sort_fn (line 396) | def tree_sort_fn(tree):
  function _populate (line 400) | def _populate(after_id = None, estimate=54301242):

FILE: r2/r2/lib/configparse.py
  class ConfigValue (line 26) | class ConfigValue(object):
    method str (line 30) | def str(v, key=None):
    method int (line 34) | def int(v, key=None):
    method float (line 38) | def float(v, key=None):
    method bool (line 42) | def bool(v, key=None):
    method tuple (line 51) | def tuple(v, key=None):
    method set (line 55) | def set(v, key=None):
    method set_of (line 59) | def set_of(value_type, delim=','):
    method tuple_of (line 66) | def tuple_of(value_type, delim=','):
    method dict (line 73) | def dict(key_type, value_type, delim=',', kvdelim=':'):
    method choice (line 81) | def choice(**choices):
    method to_iter (line 90) | def to_iter(v, delim = ','):
    method timeinterval (line 94) | def timeinterval(v, key=None):
    method messages (line 104) | def messages(v, key=None):
    method baseplate (line 108) | def baseplate(baseplate_parser):
  class ConfigValueParser (line 114) | class ConfigValueParser(dict):
    method __init__ (line 115) | def __init__(self, raw_data):
    method add_spec (line 120) | def add_spec(self, spec):
    method _update_values (line 130) | def _update_values(self, keys):

FILE: r2/r2/lib/contrib/ipaddress.py
  class AddressValueError (line 33) | class AddressValueError(ValueError):
  class NetmaskValueError (line 37) | class NetmaskValueError(ValueError):
  function ip_address (line 41) | def ip_address(address, version=None):
  function ip_network (line 81) | def ip_network(address, version=None, strict=True):
  function ip_interface (line 121) | def ip_interface(address, version=None):
  function v4_int_to_packed (line 165) | def v4_int_to_packed(address):
  function v6_int_to_packed (line 183) | def v6_int_to_packed(address):
  function _find_address_range (line 195) | def _find_address_range(addresses):
  function _get_prefix_length (line 213) | def _get_prefix_length(number1, number2, bits):
  function _count_righthand_zero_bits (line 230) | def _count_righthand_zero_bits(number, bits):
  function summarize_address_range (line 248) | def summarize_address_range(first, last):
  function _collapse_addresses_recursive (line 311) | def _collapse_addresses_recursive(addresses):
  function collapse_addresses (line 357) | def collapse_addresses(addresses):
  function get_mixed_type_key (line 414) | def get_mixed_type_key(obj):
  class _IPAddressBase (line 439) | class _IPAddressBase(object):
    method exploded (line 444) | def exploded(self):
    method compressed (line 449) | def compressed(self):
    method _ip_int_from_prefix (line 453) | def _ip_int_from_prefix(self, prefixlen=None):
    method _prefix_from_ip_int (line 467) | def _prefix_from_ip_int(self, ip_int, mask=32):
    method _ip_string_from_prefix (line 486) | def _ip_string_from_prefix(self, prefixlen=None):
  class _BaseAddress (line 501) | class _BaseAddress(_IPAddressBase):
    method __init__ (line 510) | def __init__(self, address):
    method __index__ (line 515) | def __index__(self):
    method __int__ (line 518) | def __int__(self):
    method __hex__ (line 521) | def __hex__(self):
    method __eq__ (line 524) | def __eq__(self, other):
    method __ne__ (line 531) | def __ne__(self, other):
    method __le__ (line 537) | def __le__(self, other):
    method __ge__ (line 543) | def __ge__(self, other):
    method __lt__ (line 549) | def __lt__(self, other):
    method __gt__ (line 560) | def __gt__(self, other):
    method __add__ (line 573) | def __add__(self, other):
    method __sub__ (line 578) | def __sub__(self, other):
    method __repr__ (line 583) | def __repr__(self):
    method __str__ (line 586) | def __str__(self):
    method __hash__ (line 589) | def __hash__(self):
    method _get_address_key (line 592) | def _get_address_key(self):
    method version (line 596) | def version(self):
  class _BaseNetwork (line 600) | class _BaseNetwork(_IPAddressBase):
    method __init__ (line 609) | def __init__(self, address):
    method __index__ (line 612) | def __index__(self):
    method __int__ (line 615) | def __int__(self):
    method __repr__ (line 618) | def __repr__(self):
    method hosts (line 621) | def hosts(self):
    method __iter__ (line 634) | def __iter__(self):
    method __getitem__ (line 641) | def __getitem__(self, n):
    method __lt__ (line 654) | def __lt__(self, other):
    method __gt__ (line 667) | def __gt__(self, other):
    method __le__ (line 680) | def __le__(self, other):
    method __ge__ (line 686) | def __ge__(self, other):
    method __eq__ (line 692) | def __eq__(self, other):
    method __ne__ (line 700) | def __ne__(self, other):
    method __str__ (line 706) | def __str__(self):
    method __hash__ (line 710) | def __hash__(self):
    method __contains__ (line 713) | def __contains__(self, other):
    method overlaps (line 726) | def overlaps(self, other):
    method broadcast_address (line 734) | def broadcast_address(self):
    method hostmask (line 743) | def hostmask(self):
    method network (line 752) | def network(self):
    method with_prefixlen (line 757) | def with_prefixlen(self):
    method with_netmask (line 761) | def with_netmask(self):
    method with_hostmask (line 765) | def with_hostmask(self):
    method num_addresses (line 769) | def num_addresses(self):
    method version (line 774) | def version(self):
    method prefixlen (line 778) | def prefixlen(self):
    method address_exclude (line 781) | def address_exclude(self, other):
    method compare_networks (line 863) | def compare_networks(self, other):
    method _get_networks_key (line 911) | def _get_networks_key(self):
    method subnets (line 921) | def subnets(self, prefixlen_diff=1, new_prefix=None):
    method masked (line 983) | def masked(self):
    method supernet (line 988) | def supernet(self, prefixlen_diff=1, new_prefix=None):
  class _BaseV4 (line 1032) | class _BaseV4(object):
    method __init__ (line 1045) | def __init__(self, address):
    method _explode_shorthand_ip_string (line 1049) | def _explode_shorthand_ip_string(self):
    method _ip_int_from_string (line 1052) | def _ip_int_from_string(self, ip_str):
    method _parse_octet (line 1077) | def _parse_octet(self, octet_str):
    method _string_from_ip_int (line 1100) | def _string_from_ip_int(self, ip_int):
    method max_prefixlen (line 1117) | def max_prefixlen(self):
    method version (line 1121) | def version(self):
    method is_reserved (line 1125) | def is_reserved(self):
    method is_private (line 1140) | def is_private(self):
    method is_multicast (line 1162) | def is_multicast(self):
    method is_unspecified (line 1177) | def is_unspecified(self):
    method is_loopback (line 1192) | def is_loopback(self):
    method is_link_local (line 1207) | def is_link_local(self):
  class IPv4Address (line 1221) | class IPv4Address(_BaseV4, _BaseAddress):
    method __init__ (line 1225) | def __init__(self, address):
    method packed (line 1263) | def packed(self):
  class IPv4Interface (line 1268) | class IPv4Interface(IPv4Address):
    method __init__ (line 1273) | def __init__(self, address):
    method __str__ (line 1292) | def __str__(self):
    method __eq__ (line 1296) | def __eq__(self, other):
    method __hash__ (line 1303) | def __hash__(self):
    method _is_valid_netmask (line 1306) | def _is_valid_netmask(self, netmask):
    method _is_hostmask (line 1332) | def _is_hostmask(self, ip_str):
    method prefixlen (line 1355) | def prefixlen(self):
    method ip (line 1359) | def ip(self):
    method with_prefixlen (line 1363) | def with_prefixlen(self):
    method with_netmask (line 1367) | def with_netmask(self):
    method with_hostmask (line 1371) | def with_hostmask(self):
  class IPv4Network (line 1376) | class IPv4Network(_BaseV4, _BaseNetwork):
    method __init__ (line 1392) | def __init__(self, address, strict=True):
    method packed (line 1499) | def packed(self):
    method __str__ (line 1503) | def __str__(self):
    method _is_valid_netmask (line 1507) | def _is_valid_netmask(self, netmask):
    method _is_hostmask (line 1533) | def _is_hostmask(self, ip_str):
    method with_prefixlen (line 1555) | def with_prefixlen(self):
    method with_netmask (line 1559) | def with_netmask(self):
    method with_hostmask (line 1563) | def with_hostmask(self):
  class _BaseV6 (line 1567) | class _BaseV6(object):
    method __init__ (line 1580) | def __init__(self, address):
    method _ip_int_from_string (line 1584) | def _ip_int_from_string(self, ip_str):
    method _parse_hextet (line 1663) | def _parse_hextet(self, hextet_str):
    method _compress_hextets (line 1686) | def _compress_hextets(self, hextets):
    method _string_from_ip_int (line 1733) | def _string_from_ip_int(self, ip_int=None):
    method _explode_shorthand_ip_string (line 1760) | def _explode_shorthand_ip_string(self):
    method max_prefixlen (line 1788) | def max_prefixlen(self):
    method packed (line 1792) | def packed(self):
    method version (line 1797) | def version(self):
    method is_multicast (line 1801) | def is_multicast(self):
    method is_reserved (line 1816) | def is_reserved(self):
    method is_link_local (line 1839) | def is_link_local(self):
    method is_site_local (line 1853) | def is_site_local(self):
    method is_private (line 1871) | def is_private(self):
    method ipv4_mapped (line 1886) | def ipv4_mapped(self):
    method teredo (line 1899) | def teredo(self):
    method sixtofour (line 1914) | def sixtofour(self):
    method is_unspecified (line 1927) | def is_unspecified(self):
    method is_loopback (line 1941) | def is_loopback(self):
  class IPv6Address (line 1958) | class IPv6Address(_BaseV6, _BaseAddress):
    method __init__ (line 1963) | def __init__(self, address):
  class IPv6Interface (line 2006) | class IPv6Interface(IPv6Address):
    method __init__ (line 2008) | def __init__(self, address):
    method __str__ (line 2023) | def __str__(self):
    method __eq__ (line 2027) | def __eq__(self, other):
    method __hash__ (line 2034) | def __hash__(self):
    method prefixlen (line 2038) | def prefixlen(self):
    method ip (line 2041) | def ip(self):
    method with_prefixlen (line 2045) | def with_prefixlen(self):
    method with_netmask (line 2049) | def with_netmask(self):
    method with_hostmask (line 2052) | def with_hostmask(self):
  class IPv6Network (line 2057) | class IPv6Network(_BaseV6, _BaseNetwork):
    method __init__ (line 2070) | def __init__(self, address, strict=True):
    method __str__ (line 2162) | def __str__(self):
    method _is_valid_netmask (line 2166) | def _is_valid_netmask(self, prefixlen):
    method with_netmask (line 2184) | def with_netmask(self):
    method with_prefixlen (line 2188) | def with_prefixlen(self):
    method with_netmask (line 2192) | def with_netmask(self):
    method with_hostmask (line 2196) | def with_hostmask(self):

FILE: r2/r2/lib/contrib/rcssmin.py
  function _make_cssmin (line 80) | def _make_cssmin(python_only=False):
  function main (line 345) | def main():

FILE: r2/r2/lib/contrib/simpleflake.py
  function pad_bytes_to_64 (line 46) | def pad_bytes_to_64(string):
  function binary (line 50) | def binary(num, padding=True):
  function extract_bits (line 58) | def extract_bits(data, shift, length):
  function simpleflake (line 66) | def simpleflake(timestamp=None, random_bits=None, epoch=SIMPLEFLAKE_EPOCH):
  function parse_simpleflake (line 80) | def parse_simpleflake(flake):

FILE: r2/r2/lib/cookies.py
  class Cookies (line 36) | class Cookies(dict):
    method add (line 37) | def add(self, name, value, *k, **kw):
  class Cookie (line 42) | class Cookie(object):
    method __init__ (line 43) | def __init__(self, value, expires=None, domain=None,
    method classify (line 56) | def classify(cookie_name):
    method __repr__ (line 80) | def __repr__(self):
  function have_secure_session_cookie (line 90) | def have_secure_session_cookie():
  function change_user_cookie_security (line 95) | def change_user_cookie_security(secure, remember):
  function set_secure_session_cookie (line 125) | def set_secure_session_cookie(remember=False):
  function delete_secure_session_cookie (line 135) | def delete_secure_session_cookie():
  function upgrade_cookie_security (line 143) | def upgrade_cookie_security():

FILE: r2/r2/lib/count.py
  function incr_counts (line 34) | def incr_counts(wrapped):
  function get_link_counts (line 37) | def get_link_counts(period = count_period):
  function get_sr_counts (line 42) | def get_sr_counts():

FILE: r2/r2/lib/csrf.py
  class CSRFPreventionException (line 24) | class CSRFPreventionException(Exception):
  function csrf_exempt (line 28) | def csrf_exempt(fn):
  function check_controller_csrf_prevention (line 34) | def check_controller_csrf_prevention(controller):

FILE: r2/r2/lib/cssfilter.py
  function strip_vendor_prefix (line 390) | def strip_vendor_prefix(identifier):
  class ValidationError (line 397) | class ValidationError(object):
    method __init__ (line 398) | def __init__(self, line_number, error_code, message_params=None):
    method offending_line (line 405) | def offending_line(self):
    method message_key (line 409) | def message_key(self):
  class StylesheetValidator (line 413) | class StylesheetValidator(object):
    method __init__ (line 414) | def __init__(self, images):
    method validate_url (line 417) | def validate_url(self, url_node):
    method validate_function (line 430) | def validate_function(self, function_node):
    method validate_block (line 447) | def validate_block(self, block):
    method validate_component_values (line 450) | def validate_component_values(self, component_values):
    method validate_declaration (line 462) | def validate_declaration(self, declaration):
    method validate_declaration_list (line 468) | def validate_declaration_list(self, declarations):
    method validate_qualified_rule (line 474) | def validate_qualified_rule(self, rule):
    method validate_at_rule (line 480) | def validate_at_rule(self, rule):
    method validate_rule_list (line 496) | def validate_rule_list(self, rules):
    method validate_list (line 502) | def validate_list(self, nodes, validators_by_type, ignored_types=None):
    method check_for_evil_codepoints (line 531) | def check_for_evil_codepoints(self, source_lines):
    method parse_and_validate (line 546) | def parse_and_validate(self, stylesheet_source):
  function validate_css (line 571) | def validate_css(stylesheet, images):

FILE: r2/r2/lib/db/alter_db.py
  function thing_tables (line 26) | def thing_tables():
  function rel_tables (line 33) | def rel_tables():
  function dtables (line 37) | def dtables():
  function exec_all (line 44) | def exec_all(command, data=False, rel = False, print_only = False):

FILE: r2/r2/lib/db/operators.py
  class BooleanOp (line 23) | class BooleanOp(object):
    method __init__ (line 24) | def __init__(self, *ops):
    method __repr__ (line 27) | def __repr__(self):
  class or_ (line 30) | class or_(BooleanOp): pass
  class and_ (line 31) | class and_(BooleanOp): pass
  class not_ (line 32) | class not_(BooleanOp): pass
  class op (line 34) | class op(object):
    method __init__ (line 35) | def __init__(self, lval, lval_name, rval):
    method __repr__ (line 40) | def __repr__(self):
    method __cmp__ (line 44) | def __cmp__(self, other):
  class eq (line 47) | class eq(op): pass
  class ne (line 48) | class ne(op): pass
  class lt (line 49) | class lt(op): pass
  class lte (line 50) | class lte(op): pass
  class gt (line 51) | class gt(op): pass
  class gte (line 52) | class gte(op): pass
  class in_ (line 53) | class in_(op): pass
  class Slot (line 55) | class Slot(object):
    method __init__ (line 56) | def __init__(self, lval):
    method __repr__ (line 63) | def __repr__(self):
    method __eq__ (line 66) | def __eq__(self, other):
    method __ne__ (line 69) | def __ne__(self, other):
    method __lt__ (line 72) | def __lt__(self, other):
    method __le__ (line 75) | def __le__(self, other):
    method __gt__ (line 78) | def __gt__(self, other):
    method __ge__ (line 81) | def __ge__(self, other):
    method in_ (line 84) | def in_(self, other):
  class Slots (line 87) | class Slots(object):
    method __getattr__ (line 88) | def __getattr__(self, attr):
    method __getitem__ (line 91) | def __getitem__(self, attr):
  function op_iter (line 94) | def op_iter(ops):
  class query_func (line 102) | class query_func(Slot): pass
  class lower (line 103) | class lower(query_func): pass
  class ip_network (line 104) | class ip_network(query_func): pass
  class base_url (line 105) | class base_url(query_func): pass
  class domain (line 106) | class domain(query_func): pass
  class year_func (line 107) | class year_func(query_func): pass
  class timeago (line 109) | class timeago(object):
    method __init__ (line 110) | def __init__(self, interval):
    method __repr__ (line 113) | def __repr__(self):
  class sort (line 116) | class sort(object):
    method __init__ (line 117) | def __init__(self, col):
    method __repr__ (line 120) | def __repr__(self):
    method __eq__ (line 123) | def __eq__(self, other):
    method __ne__ (line 126) | def __ne__(self, other):
  class asc (line 130) | class asc(sort): pass
  class desc (line 131) | class desc(sort):pass
  class shuffled (line 132) | class shuffled(desc): pass

FILE: r2/r2/lib/db/queries.py
  function db_sort (line 88) | def db_sort(sort):
  function filter_identity (line 106) | def filter_identity(x):
  function filter_thing2 (line 109) | def filter_thing2(x):
  class CachedResults (line 115) | class CachedResults(object):
    method __init__ (line 118) | def __init__(self, query, filter):
    method get_query_iden (line 128) | def get_query_iden(cls, query):
    method sort (line 144) | def sort(self):
    method fetch (line 147) | def fetch(self, force=False, stale=False):
    method fetch_multi (line 152) | def fetch_multi(cls, crs, force=False, stale=False):
    method make_item_tuple (line 167) | def make_item_tuple(self, item):
    method can_insert (line 183) | def can_insert(self):
    method can_delete (line 204) | def can_delete(self):
    method _mutate (line 208) | def _mutate(self, fn, willread=True):
    method insert (line 217) | def insert(self, items):
    method _insert_tuples (line 222) | def _insert_tuples(self, tuples):
    method delete (line 257) | def delete(self, items):
    method _replace (line 268) | def _replace(self, tuples, lock=True):
    method update (line 281) | def update(self):
    method __repr__ (line 288) | def __repr__(self):
    method __iter__ (line 291) | def __iter__(self):
  class MergedCachedResults (line 297) | class MergedCachedResults(object):
    method __init__ (line 304) | def __init__(self, results):
    method __repr__ (line 324) | def __repr__(self):
    method __iter__ (line 327) | def __iter__(self):
    method update (line 331) | def update(self):
  function make_results (line 335) | def make_results(query, filter = filter_identity):
  function merge_results (line 338) | def merge_results(*results):
  function migrating_cached_query (line 343) | def migrating_cached_query(model, filter_fn=filter_identity):
  function get_deleted_links (line 362) | def get_deleted_links(user_id):
  function get_deleted_comments (line 370) | def get_deleted_comments(user_id):
  function get_deleted (line 378) | def get_deleted(user):
  function get_links (line 383) | def get_links(sr, sort, time):
  function _get_links (line 386) | def _get_links(sr_id, sort, time):
  function get_spam_links (line 400) | def get_spam_links(sr_id):
  function get_spam_comments (line 406) | def get_spam_comments(sr_id):
  function get_edited_comments (line 413) | def get_edited_comments(sr_id):
  function get_edited_links (line 418) | def get_edited_links(sr_id):
  function get_edited (line 423) | def get_edited(sr, user=None, include_links=True, include_comments=True):
  function moderated_srids (line 434) | def moderated_srids(sr, user):
  function get_spam (line 445) | def get_spam(sr, user=None, include_links=True, include_comments=True):
  function get_spam_filtered_links (line 456) | def get_spam_filtered_links(sr_id):
  function get_spam_filtered_comments (line 465) | def get_spam_filtered_comments(sr_id):
  function get_spam_filtered (line 472) | def get_spam_filtered(sr):
  function get_reported_links (line 477) | def get_reported_links(sr_id):
  function get_reported_comments (line 486) | def get_reported_comments(sr_id):
  function get_reported (line 496) | def get_reported(sr, user=None, include_links=True, include_comments=True):
  function get_unmoderated_links (line 507) | def get_unmoderated_links(sr_id):
  function get_modqueue (line 518) | def get_modqueue(sr, user=None, include_links=True, include_comments=True):
  function get_unmoderated (line 531) | def get_unmoderated(sr, user=None):
  function get_domain_links (line 536) | def get_domain_links(domain, sort, time):
  function user_query (line 546) | def user_query(kind, user_id, sort, time):
  function get_all_comments (line 555) | def get_all_comments():
  function get_sr_comments (line 560) | def get_sr_comments(sr):
  function _get_sr_comments (line 563) | def _get_sr_comments(sr_id):
  function _get_comments (line 569) | def _get_comments(user_id, sort, time):
  function get_comments (line 572) | def get_comments(user, sort, time):
  function _get_submitted (line 575) | def _get_submitted(user_id, sort, time):
  function get_submitted (line 578) | def get_submitted(user, sort, time):
  function get_user_actions (line 582) | def get_user_actions(user, sort, time):
  function get_overview (line 602) | def get_overview(user, sort, time):
  function rel_query (line 606) | def rel_query(rel, thing_id, name, filters = []):
  function get_liked (line 624) | def get_liked(user):
  function get_disliked (line 628) | def get_disliked(user):
  function get_hidden_links (line 632) | def get_hidden_links(user_id):
  function get_hidden (line 635) | def get_hidden(user):
  function get_categorized_saved_links (line 639) | def get_categorized_saved_links(user_id, sr_id, category):
  function get_categorized_saved_comments (line 643) | def get_categorized_saved_comments(user_id, sr_id, category):
  function get_saved_links (line 647) | def get_saved_links(user_id, sr_id):
  function get_saved_comments (line 651) | def get_saved_comments(user_id, sr_id):
  function get_saved (line 654) | def get_saved(user, sr_id=None, category=None):
  function get_subreddit_messages (line 665) | def get_subreddit_messages(sr):
  function get_unread_subreddit_messages (line 669) | def get_unread_subreddit_messages(sr):
  function get_unread_subreddit_messages_multi (line 673) | def get_unread_subreddit_messages_multi(srs):
  function get_inbox_messages (line 681) | def get_inbox_messages(user):
  function get_unread_messages (line 685) | def get_unread_messages(user):
  function get_inbox_comments (line 691) | def get_inbox_comments(user):
  function get_unread_comments (line 695) | def get_unread_comments(user):
  function get_inbox_selfreply (line 700) | def get_inbox_selfreply(user):
  function get_unread_selfreply (line 704) | def get_unread_selfreply(user):
  function get_inbox_comment_mentions (line 710) | def get_inbox_comment_mentions(user):
  function get_unread_comment_mentions (line 715) | def get_unread_comment_mentions(user):
  function get_inbox (line 720) | def get_inbox(user):
  function get_sent (line 727) | def get_sent(user_id):
  function get_unread_inbox (line 732) | def get_unread_inbox(user):
  function _user_reported_query (line 738) | def _user_reported_query(user_id, thing_cls):
  function get_user_reported_links (line 746) | def get_user_reported_links(user_id):
  function get_user_reported_comments (line 750) | def get_user_reported_comments(user_id):
  function get_user_reported_messages (line 754) | def get_user_reported_messages(user_id):
  function get_user_reported (line 758) | def get_user_reported(user_id):
  function set_promote_status (line 764) | def set_promote_status(link, promote_status):
  function _promoted_link_query (line 805) | def _promoted_link_query(user_id, status):
  function get_unpaid_links (line 826) | def get_unpaid_links(user_id):
  function get_all_unpaid_links (line 831) | def get_all_unpaid_links():
  function get_unapproved_links (line 836) | def get_unapproved_links(user_id):
  function get_all_unapproved_links (line 841) | def get_all_unapproved_links():
  function get_rejected_links (line 846) | def get_rejected_links(user_id):
  function get_all_rejected_links (line 851) | def get_all_rejected_links():
  function get_live_links (line 856) | def get_live_links(user_id):
  function get_all_live_links (line 861) | def get_all_live_links():
  function get_accepted_links (line 866) | def get_accepted_links(user_id):
  function get_all_accepted_links (line 871) | def get_all_accepted_links():
  function get_edited_live_links (line 876) | def get_edited_live_links(user_id):
  function get_all_edited_live_links (line 881) | def get_all_edited_live_links():
  function get_payment_flagged_links (line 886) | def get_payment_flagged_links():
  function set_payment_flagged_link (line 890) | def set_payment_flagged_link(link):
  function unset_payment_flagged_link (line 896) | def unset_payment_flagged_link(link):
  function get_underdelivered_campaigns (line 903) | def get_underdelivered_campaigns():
  function set_underdelivered_campaigns (line 907) | def set_underdelivered_campaigns(campaigns):
  function unset_underdelivered_campaigns (line 914) | def unset_underdelivered_campaigns(campaigns):
  function get_promoted_links (line 922) | def get_promoted_links(user_id):
  function get_all_promoted_links (line 930) | def get_all_promoted_links():
  function get_all_gilded_comments (line 938) | def get_all_gilded_comments():
  function get_all_gilded_links (line 943) | def get_all_gilded_links():
  function get_all_gilded (line 948) | def get_all_gilded():
  function get_gilded_comments (line 953) | def get_gilded_comments(sr_id):
  function get_gilded_links (line 958) | def get_gilded_links(sr_id):
  function get_gilded (line 963) | def get_gilded(sr_ids):
  function get_gilded_user_comments (line 970) | def get_gilded_user_comments(user_id):
  function get_gilded_user_links (line 975) | def get_gilded_user_links(user_id):
  function get_gilded_users (line 980) | def get_gilded_users(user_ids):
  function get_user_gildings (line 987) | def get_user_gildings(user_id):
  function get_gilded_user (line 992) | def get_gilded_user(user):
  function add_queries (line 996) | def add_queries(queries, insert_items=None, delete_items=None):
  function all_queries (line 1026) | def all_queries(fn, obj, *param_lists):
  function new_link (line 1045) | def new_link(link):
  function add_to_commentstree_q (line 1067) | def add_to_commentstree_q(comment):
  function update_comment_notifications (line 1077) | def update_comment_notifications(comment, inbox_rels):
  function new_comment (line 1103) | def new_comment(comment, inbox_rels):
  function new_subreddit (line 1137) | def new_subreddit(sr):
  function new_message (line 1142) | def new_message(message, inbox_rels, add_to_sent=True, update_modmail=Tr...
  function set_unread (line 1194) | def set_unread(messages, user, unread, mutator=None):
  function update_unread_queries (line 1203) | def update_unread_queries(inbox_rels, insert=True, mutator=None):
  function set_sr_unread (line 1236) | def set_sr_unread(messages, sr, unread, mutator=None):
  function update_unread_sr_queries (line 1245) | def update_unread_sr_queries(inbox_rels, insert=True, mutator=None):
  function unread_handler (line 1265) | def unread_handler(things, user, unread):
  function unnotify (line 1304) | def unnotify(thing, possible_recipients=None):
  function renotify (line 1319) | def renotify(thing, possible_recipients=None):
  function notification_handler (line 1335) | def notification_handler(thing, notify_function,
  function _by_srid (line 1379) | def _by_srid(things, srs=True):
  function _by_author (line 1395) | def _by_author(things, authors=True):
  function _by_thing1_id (line 1409) | def _by_thing1_id(rels):
  function was_spam_filtered (line 1416) | def was_spam_filtered(thing):
  function delete (line 1424) | def delete(things):
  function edit (line 1479) | def edit(thing):
  function ban (line 1490) | def ban(things, filtered=True):
  function _common_del_ban (line 1546) | def _common_del_ban(things):
  function unban (line 1571) | def unban(things, insert=True):
  function new_report (line 1627) | def new_report(thing, report_rel):
  function clear_reports (line 1652) | def clear_reports(things, rels):
  function add_all_srs (line 1692) | def add_all_srs():
  function update_user (line 1706) | def update_user(user):
  function add_all_users (line 1723) | def add_all_users():
  function run_new_comments (line 1730) | def run_new_comments(limit=1000):
  function run_commentstree (line 1750) | def run_commentstree(qname="commentstree_q", limit=400):
  function _by_type (line 1768) | def _by_type(items):
  function get_stored_votes (line 1775) | def get_stored_votes(user, things):
  function get_likes (line 1794) | def get_likes(user, requested_items):
  function consume_mark_all_read (line 1855) | def consume_mark_all_read():
  function consume_deleted_accounts (line 1867) | def consume_deleted_accounts():

FILE: r2/r2/lib/db/tdb_cassandra.py
  class CassandraException (line 85) | class CassandraException(Exception):
  class InvariantException (line 89) | class InvariantException(CassandraException):
  class ConfigurationException (line 93) | class ConfigurationException(CassandraException):
  class TdbException (line 98) | class TdbException(CassandraException):
  class NotFound (line 102) | class NotFound(CassandraException, NotFoundException):
  function will_write (line 107) | def will_write(fn):
  function get_manager (line 116) | def get_manager(seeds):
  class ThingMeta (line 121) | class ThingMeta(type):
    method __init__ (line 122) | def __init__(cls, name, bases, dct):
    method __repr__ (line 189) | def __repr__(cls):
  class ThingBase (line 193) | class ThingBase(object):
    method __init__ (line 275) | def __init__(self, _id = None, _committed = False, _partial = None, **...
    method _byID (line 300) | def _byID(cls, ids, return_dict=True, properties=None):
    method _fullname (line 392) | def _fullname(self):
    method _by_fullname (line 400) | def _by_fullname(cls, fnames, return_dict=True, ignore_missing=False):
    method _cache_prefix (line 428) | def _cache_prefix(cls):
    method _cache_key (line 431) | def _cache_key(self):
    method _cache_key_id (line 438) | def _cache_key_id(cls, t_id):
    method _wcl (line 442) | def _wcl(cls, wcl, default = None):
    method _rcl (line 449) | def _rcl(cls, rcl, default = None):
    method _get_column_validator (line 457) | def _get_column_validator(cls, colname):
    method _deserialize_column (line 462) | def _deserialize_column(cls, attr, val):
    method _serialize_column (line 486) | def _serialize_column(cls, attr, val):
    method _serialize_date (line 513) | def _serialize_date(cls, date):
    method _deserialize_date (line 517) | def _deserialize_date(cls, val):
    method _from_serialized_columns (line 531) | def _from_serialized_columns(cls, t_id, columns):
    method _from_columns (line 538) | def _from_columns(cls, t_id, columns):
    method _dirty (line 548) | def _dirty(self):
    method _commit (line 552) | def _commit(self, write_consistency_level = None):
    method _revert (line 637) | def _revert(self):
    method _destroy (line 645) | def _destroy(self):
    method __getattr__ (line 649) | def __getattr__(self, attr):
    method __setattr__ (line 671) | def __setattr__(self, attr, val):
    method __eq__ (line 688) | def __eq__(self, other):
    method __ne__ (line 697) | def __ne__(self, other):
    method _t (line 701) | def _t(self):
    method __getitem__ (line 719) | def __getitem__(self, key):
    method __setitem__ (line 722) | def __setitem__(self, key, value):
    method __delitem__ (line 725) | def __delitem__(self, key):
    method _get (line 736) | def _get(self, key, default = None):
    method _set_ttl (line 744) | def _set_ttl(self, key, ttl):
    method _on_create (line 749) | def _on_create(self):
    method _on_commit (line 755) | def _on_commit(self):
    method _all (line 760) | def _all(cls):
    method __repr__ (line 766) | def __repr__(self):
    method __del__ (line 782) | def __del__(self):
  class Thing (line 791) | class Thing(ThingBase):
    method _date (line 796) | def _date(self):
  class UuidThing (line 799) | class UuidThing(ThingBase):
    method __init__ (line 805) | def __init__(self, **kw):
    method _byID (line 809) | def _byID(cls, ids, **kw):
    method _cache_key_id (line 824) | def _cache_key_id(cls, t_id):
  function view_of (line 828) | def view_of(cls):
  class DenormalizedRelation (line 845) | class DenormalizedRelation(object):
    method value_for (line 871) | def value_for(cls, thing1, thing2, **kw):
    method create (line 877) | def create(cls, thing1, thing2s, **kw):
    method destroy (line 901) | def destroy(cls, thing1, thing2s):
    method fast_query (line 910) | def fast_query(cls, thing1, thing2s):
  class ColumnQuery (line 955) | class ColumnQuery(object):
    method __init__ (line 961) | def __init__(self, cls, rowkeys, column_start="", column_finish="",
    method combine (line 985) | def combine(queries):
    method default_column_to_obj (line 989) | def default_column_to_obj(columns):
    method default_obj_to_column (line 999) | def default_obj_to_column(objs):
    method _after (line 1011) | def _after(self, thing):
    method _after_id (line 1018) | def _after_id(self, column_name):
    method _reverse (line 1021) | def _reverse(self):
    method __iter__ (line 1025) | def __iter__(self, yield_column_names=False):
    method __repr__ (line 1094) | def __repr__(self):
  class MultiColumnQuery (line 1098) | class MultiColumnQuery(object):
    method __init__ (line 1099) | def __init__(self, queries, num, sort_key=None):
    method _after (line 1105) | def _after(self, thing):
    method _reverse (line 1109) | def _reverse(self):
    method __setattr__ (line 1113) | def __setattr__(self, attr, val):
    method __iter__ (line 1121) | def __iter__(self):
  class Query (line 1162) | class Query(object):
    method __init__ (line 1166) | def __init__(self, cls, after=None, properties=None, limit=100,
    method __copy__ (line 1175) | def __copy__(self):
    method _dump (line 1183) | def _dump(self):
    method __iter__ (line 1192) | def __iter__(self):
  class View (line 1215) | class View(ThingBase):
    method _rowkey (line 1228) | def _rowkey(cls, obj):
    method _obj_to_column (line 1235) | def _obj_to_column(cls, objs):
    method _column_to_obj (line 1248) | def _column_to_obj(cls, columns):
    method add_object (line 1260) | def add_object(cls, obj, **kw):
    method query (line 1267) | def query(cls, rowkeys, after=None, reverse=False, count=1000):
    method _values (line 1279) | def _values(self):
    method get_time_sorted_columns (line 1285) | def get_time_sorted_columns(cls, rowkey, limit=None):
    method _set_values (line 1294) | def _set_values(cls, row_key, col_values,
    method _remove (line 1329) | def _remove(cls, key, columns):
  class DenormalizedView (line 1333) | class DenormalizedView(View):
    method is_date_prop (line 1337) | def is_date_prop(cls, attr):
    method _thing_dumper (line 1344) | def _thing_dumper(cls, thing):
    method _thing_loader (line 1358) | def _thing_loader(cls, _id, dump):
    method _obj_to_column (line 1370) | def _obj_to_column(cls, objs):
    method _column_to_obj (line 1384) | def _column_to_obj(cls, columns):

FILE: r2/r2/lib/db/tdb_lite.py
  class tdb_lite (line 26) | class tdb_lite(object):
    method __init__ (line 27) | def __init__(self, gc):
    method make_metadata (line 30) | def make_metadata(self, engine):
    method index_str (line 35) | def index_str(self, table, name, on, where = None):
    method create_table (line 43) | def create_table(self, table, index_commands=None):
    method py2db (line 53) | def py2db(self, val, return_kind=False):
    method db2py (line 72) | def db2py(self, val, kind):

FILE: r2/r2/lib/db/tdb_sql.py
  class TransactionSet (line 53) | class TransactionSet(threading.local):
    method __init__ (line 70) | def __init__(self):
    method begin (line 74) | def begin(self):
    method add_engine (line 78) | def add_engine(self, engine):
    method commit (line 87) | def commit(self):
    method rollback (line 95) | def rollback(self):
    method _clear (line 103) | def _clear(self):
  function make_metadata (line 113) | def make_metadata(engine):
  function create_table (line 118) | def create_table(table, index_commands=None):
  function index_str (line 128) | def index_str(table, name, on, where = None, unique = False):
  function index_commands (line 141) | def index_commands(table, type):
  function get_type_table (line 175) | def get_type_table(metadata):
  function get_rel_type_table (line 181) | def get_rel_type_table(metadata):
  function get_thing_table (line 189) | def get_thing_table(metadata, name):
  function get_data_table (line 212) | def get_data_table(metadata, name):
  function get_rel_table (line 222) | def get_rel_table(metadata, name):
  function make_type_table (line 235) | def make_type_table():
  function make_rel_type_table (line 242) | def make_rel_type_table():
  class ConfigurationError (line 255) | class ConfigurationError(Exception):
  function check_type (line 258) | def check_type(table, name, insert_vals):
  function build_thing_tables (line 278) | def build_thing_tables():
  function build_rel_tables (line 310) | def build_rel_tables():
  function get_write_table (line 356) | def get_write_table(tables):
  function add_request_info (line 362) | def add_request_info(select):
  function get_table (line 383) | def get_table(kind, action, tables, avoid_master_reads = False):
  function get_thing_table (line 402) | def get_thing_table(type_id, action = 'read' ):
  function get_rel_table (line 407) | def get_rel_table(rel_type_id, action = 'read'):
  function make_thing (line 414) | def make_thing(type_id, ups, downs, date, deleted, spam, id=None):
  function set_thing_props (line 445) | def set_thing_props(type_id, thing_id, **props):
  function incr_thing_prop (line 460) | def incr_thing_prop(type_id, thing_id, prop, amount):
  class CreationError (line 471) | class CreationError(Exception): pass
  function make_relation (line 475) | def make_relation(rel_type_id, thing1_id, thing2_id, name, date=None):
  function set_rel_props (line 493) | def set_rel_props(rel_type_id, rel_id, **props):
  function py2db (line 506) | def py2db(val, return_kind=False):
  function db2py (line 525) | def db2py(val, kind):
  function update_data (line 541) | def update_data(table, thing_id, **vals):
  function create_data (line 561) | def create_data(table, thing_id, **vals):
  function incr_data_prop (line 574) | def incr_data_prop(table, type_id, thing_id, prop, amount):
  function fetch_query (line 582) | def fetch_query(table, id_col, thing_id):
  function get_data (line 602) | def get_data(table, thing_id):
  function set_thing_data (line 618) | def set_thing_data(type_id, thing_id, brand_new_thing, **vals):
  function incr_thing_data (line 626) | def incr_thing_data(type_id, thing_id, prop, amount):
  function get_thing_data (line 630) | def get_thing_data(type_id, thing_id):
  function get_thing (line 634) | def get_thing(type_id, thing_id):
  function set_rel_data (line 657) | def set_rel_data(rel_type_id, thing_id, brand_new_thing, **vals):
  function incr_rel_data (line 665) | def incr_rel_data(rel_type_id, thing_id, prop, amount):
  function get_rel_data (line 669) | def get_rel_data(rel_type_id, rel_id):
  function get_rel (line 673) | def get_rel(rel_type_id, rel_id):
  function del_rel (line 689) | def del_rel(rel_type_id, rel_id):
  function sa_op (line 700) | def sa_op(op):
  function translate_sort (line 732) | def translate_sort(table, column_name, lval = None, rewrite_name = True):
  function add_sort (line 754) | def add_sort(sort, t_table, select):
  function translate_thing_value (line 799) | def translate_thing_value(rval):
  function find_things (line 806) | def find_things(type_id, sort, limit, offset, constraints):
  function translate_data_value (line 839) | def translate_data_value(alias, op):
  function find_data (line 856) | def find_data(type_id, sort, limit, offset, constraints):
  function sort_thing_ids_by_data_value (line 926) | def sort_thing_ids_by_data_value(type_id, thing_ids, value_name,
  function find_rels (line 960) | def find_rels(ret_props, rel_type_id, sort, limit, offset, constraints):

FILE: r2/r2/lib/db/thing.py
  class NotFound (line 40) | class NotFound(Exception):
  class SafeSetAttr (line 50) | class SafeSetAttr:
    method __init__ (line 51) | def __init__(self, cls):
    method __enter__ (line 54) | def __enter__(self):
    method __exit__ (line 57) | def __exit__(self, type, value, tb):
  class TdbTransactionContext (line 61) | class TdbTransactionContext(object):
    method __enter__ (line 62) | def __enter__(self):
    method __exit__ (line 65) | def __exit__(self, exc_type, exc_value, traceback):
  class DataThing (line 73) | class DataThing(object):
    method __init__ (line 85) | def __init__(self):
    method __setattr__ (line 94) | def __setattr__(self, attr, val, make_dirty=True):
    method __setstate__ (line 112) | def __setstate__(self, state):
    method __getattr__ (line 120) | def __getattr__(self, attr):
    method _cache_prefix (line 154) | def _cache_prefix(cls):
    method _cache_key (line 157) | def _cache_key(self):
    method get_things_from_db (line 162) | def get_things_from_db(cls, ids):
    method get_things_from_cache (line 167) | def get_things_from_cache(cls, ids, stale=False, allow_local=True):
    method write_things_to_cache (line 177) | def write_things_to_cache(cls, things_by_id):
    method get_read_modify_write_lock (line 193) | def get_read_modify_write_lock(self):
    method write_new_thing_to_db (line 204) | def write_new_thing_to_db(self):
    method write_props_to_db (line 208) | def write_props_to_db(self, props, data_props, brand_new_thing):
    method write_changes_to_db (line 212) | def write_changes_to_db(self, changes, brand_new_thing=False):
    method write_thing_to_cache (line 227) | def write_thing_to_cache(self, lock, brand_new_thing=False):
    method update_from_cache (line 241) | def update_from_cache(self, lock):
    method record_cache_write (line 274) | def record_cache_write(cls, event, delta=1):
    method _commit (line 277) | def _commit(self):
    method _incr (line 344) | def _incr(self, prop, amt=1):
    method _id36 (line 348) | def _id36(self):
    method _fullname_prefix (line 352) | def _fullname_prefix(cls):
    method _fullname_from_id36 (line 356) | def _fullname_from_id36(cls, id36):
    method _fullname (line 360) | def _fullname(self):
    method _byID (line 364) | def _byID(cls, ids, data=True, return_dict=True, stale=False,
    method _byID36 (line 416) | def _byID36(cls, id36s, return_dict = True, **kw):
    method _by_fullname (line 434) | def _by_fullname(cls, names,
    method _dirty (line 485) | def _dirty(self):
    method _query (line 489) | def _query(cls, *a, **kw):
  class ThingMeta (line 493) | class ThingMeta(type):
    method __init__ (line 494) | def __init__(cls, name, bases, dct):
    method __repr__ (line 511) | def __repr__(cls):
  class Thing (line 514) | class Thing(DataThing):
    method __init__ (line 522) | def __init__(self, ups = 0, downs = 0, date = None, deleted = False,
    method record_cache_write (line 547) | def record_cache_write(cls, event, delta=1):
    method __repr__ (line 552) | def __repr__(self):
    method get_things_from_db (line 557) | def get_things_from_db(cls, ids):
    method write_new_thing_to_db (line 588) | def write_new_thing_to_db(self):
    method write_props_to_db (line 602) | def write_props_to_db(self, props, data_props, brand_new_thing):
    method _incr (line 621) | def _incr(self, prop, amt=1):
    method _age (line 673) | def _age(self):
    method _hot (line 677) | def _hot(self):
    method _score (line 681) | def _score(self):
    method _controversy (line 685) | def _controversy(self):
    method _confidence (line 689) | def _confidence(self):
    method num_votes (line 693) | def num_votes(self):
    method is_distinguished (line 697) | def is_distinguished(self):
    method _query (line 707) | def _query(cls, *all_rules, **kw):
    method sort_ids_by_data_value (line 739) | def sort_ids_by_data_value(cls, thing_ids, value_name,
    method update_search_index (line 744) | def update_search_index(self, boost_only=False):
  class RelationMeta (line 754) | class RelationMeta(type):
    method __init__ (line 755) | def __init__(cls, name, bases, dct):
    method __repr__ (line 773) | def __repr__(cls):
  function Relation (line 776) | def Relation(type1, type2):
  class Query (line 1084) | class Query(object):
    method __init__ (line 1085) | def __init__(self, kind, *rules, **kw):
    method _setsort (line 1100) | def _setsort(self, sorts):
    method _getsort (line 1117) | def _getsort(self):
    method _reverse (line 1122) | def _reverse(self):
    method _list (line 1129) | def _list(self, data=True):
    method _dir (line 1132) | def _dir(self, thing, reverse):
    method _before (line 1166) | def _before(self, thing):
    method _after (line 1169) | def _after(self, thing):
    method _filter (line 1172) | def _filter(*a, **kw):
    method _cursor (line 1175) | def _cursor(*a, **kw):
    method _cache_key (line 1178) | def _cache_key(self):
    method _get_results (line 1192) | def _get_results(self):
    method get_from_cache (line 1196) | def get_from_cache(self, allow_local=True):
    method set_to_cache (line 1204) | def set_to_cache(self, things):
    method __iter__ (line 1208) | def __iter__(self):
  class Things (line 1236) | class Things(Query):
    method __init__ (line 1237) | def __init__(self, kind, *rules, **kw):
    method _filter (line 1241) | def _filter(self, *rules):
    method _cursor (line 1249) | def _cursor(self):
  function load_things (line 1269) | def load_things(rels, stale=False):
  class Relations (line 1287) | class Relations(Query):
    method __init__ (line 1288) | def __init__(self, kind, *rules, **kw):
    method _filter (line 1293) | def _filter(self, *rules):
    method _eager (line 1297) | def _eager(self, eager):
    method _make_rel (line 1302) | def _make_rel(self, rows):
    method _cursor (line 1309) | def _cursor(self):
  class RelationsPropsOnly (line 1321) | class RelationsPropsOnly(Relations):
    method __init__ (line 1322) | def __init__(self, kind, props, *rules, **kw):
    method _cursor (line 1326) | def _cursor(self):
    method _cache_key (line 1337) | def _cache_key(self):
    method _get_results (line 1352) | def _get_results(self):
    method get_from_cache (line 1356) | def get_from_cache(self, allow_local=True):
    method set_to_cache (line 1359) | def set_to_cache(self, rows):
  class MultiCursor (line 1363) | class MultiCursor(object):
    method __init__ (line 1364) | def __init__(self, *execute_params):
    method fetchone (line 1368) | def fetchone(self):
    method fetchall (line 1374) | def fetchall(self):
  class MergeCursor (line 1380) | class MergeCursor(MultiCursor):
    method _execute (line 1381) | def _execute(self, cursors, sorts):
  class MultiQuery (line 1437) | class MultiQuery(Query):
    method __init__ (line 1438) | def __init__(self, queries, *rules, **kw):
    method get_from_cache (line 1445) | def get_from_cache(self):
    method set_to_cache (line 1448) | def set_to_cache(self):
    method _cursor (line 1451) | def _cursor(self):
    method _reverse (line 1454) | def _reverse(self):
    method _setdata (line 1458) | def _setdata(self, data):
    method _getdata (line 1461) | def _getdata(self):
    method _setsort (line 1466) | def _setsort(self, sorts):
    method _getsort (line 1470) | def _getsort(self):
    method _filter (line 1476) | def _filter(self, *rules):
    method _getrules (line 1480) | def _getrules(self):
    method _setrules (line 1483) | def _setrules(self, rules):
    method _getlimit (line 1489) | def _getlimit(self):
    method _setlimit (line 1492) | def _setlimit(self, limit):
  class Merge (line 1499) | class Merge(MultiQuery):
    method _cursor (line 1500) | def _cursor(self):
  function MultiRelation (line 1509) | def MultiRelation(name, *relations):

FILE: r2/r2/lib/db/userrel.py
  class UserRelManager (line 30) | class UserRelManager(object):
    method __init__ (line 33) | def __init__(self, name, relation, permission_class):
    method get (line 38) | def get(self, thing, user):
    method add (line 46) | def add(self, thing, user, permissions=None, **attrs):
    method remove (line 61) | def remove(self, thing, user):
    method mutate (line 68) | def mutate(self, thing, user, **attrs):
    method ids (line 79) | def ids(self, thing):
    method reverse_ids (line 88) | def reverse_ids(self, user):
    method by_thing (line 96) | def by_thing(self, thing):
  class MemoizedUserRelManager (line 109) | class MemoizedUserRelManager(UserRelManager):
    method __init__ (line 112) | def __init__(self, name, relation, permission_class,
    method _update_caches (line 128) | def _update_caches(self, thing, user):
    method _update_caches_on_success (line 134) | def _update_caches_on_success(self, method):
  function UserRel (line 147) | def UserRel(name, relation, disable_ids_fn=False, disable_reverse_ids_fn...
  function MigratingUserRel (line 186) | def MigratingUserRel(name, relation, disable_ids_fn=False,

FILE: r2/r2/lib/einhorn.py
  class EinhornSyncWorker (line 57) | class EinhornSyncWorker(SyncWorker):
    method __init__ (line 58) | def __init__(self, cfg, app):
    method init_signals (line 70) | def init_signals(self):
    method start_graceful_shutdown (line 78) | def start_graceful_shutdown(self, signal_number, frame):
  function run_gunicorn_worker (line 84) | def run_gunicorn_worker():

FILE: r2/r2/lib/emailer.py
  function _system_email (line 45) | def _system_email(email, plaintext_body, kind, reply_to="",
  function _ads_email (line 64) | def _ads_email(body, from_name, kind):
  function _fraud_email (line 71) | def _fraud_email(body, kind):
  function _community_email (line 78) | def _community_email(body, kind):
  function verify_email (line 85) | def verify_email(user, dest=None):
  function password_email (line 106) | def password_email(user):
  function message_notification_email (line 141) | def message_notification_email(data):
  function generate_notification_email_unsubscribe_token (line 186) | def generate_notification_email_unsubscribe_token(user_id36, user_email=...
  function password_change_email (line 211) | def password_change_email(user):
  function email_change_email (line 221) | def email_change_email(user):
  function community_email (line 229) | def community_email(body, kind):
  function ads_email (line 233) | def ads_email(body, from_name=g.domain):
  function share (line 237) | def share(link, emails, from_name = "", reply_to = "", body = ""):
  function send_queued_mail (line 247) | def send_queued_mail(test = False):
  function opt_out (line 319) | def opt_out(msg_hash):
  function opt_in (line 327) | def opt_in(msg_hash):
  function _promo_email (line 336) | def _promo_email(thing, kind, body = "", **kw):
  function new_promo (line 350) | def new_promo(thing):
  function promo_total_budget (line 353) | def promo_total_budget(thing, total_budget_dollars, start_date):
  function accept_promo (line 357) | def accept_promo(thing):
  function reject_promo (line 360) | def reject_promo(thing, reason = ""):
  function edited_live_promo (line 363) | def edited_live_promo(thing):
  function queue_promo (line 366) | def queue_promo(thing, total_budget_dollars, trans_id):
  function live_promo (line 370) | def live_promo(thing):
  function finished_promo (line 373) | def finished_promo(thing):
  function refunded_promo (line 377) | def refunded_promo(thing):
  function void_payment (line 381) | def void_payment(thing, campaign, total_budget_dollars, reason):
  function fraud_alert (line 387) | def fraud_alert(body):
  function suspicious_payment (line 390) | def suspicious_payment(user, link):
  function send_html_email (line 399) | def send_html_email(to_addr, from_addr, subject, html,

FILE: r2/r2/lib/embeds.py
  function get_inject_template (line 30) | def get_inject_template(omitscript=False):
  function edited_after (line 42) | def edited_after(thing, iso_timestamp, showedits):
  function prepare_embed_request (line 59) | def prepare_embed_request():
  function set_up_comment_embed (line 80) | def set_up_comment_embed(sr, thing, showedits):
  function is_embed (line 110) | def is_embed():

FILE: r2/r2/lib/emr_helpers.py
  function get_live_clusters (line 35) | def get_live_clusters(emr_connection):
  function get_step_states (line 41) | def get_step_states(emr_connection, jobflowid):
  function get_step_state (line 62) | def get_step_state(emr_connection, jobflowid, step_name, update=False):
  function get_jobflow_id (line 80) | def get_jobflow_id(emr_connection, name):
  function terminate_jobflow (line 92) | def terminate_jobflow(emr_connection, jobflow_name):
  function modify_slave_count (line 98) | def modify_slave_count(emr_connection, jobflow_name, num_slaves=1):
  class EmrJob (line 119) | class EmrJob(object):
    method __init__ (line 120) | def __init__(self, emr_connection, name, steps=[], setup_steps=[],
    method run (line 148) | def run(self):
    method terminate (line 172) | def terminate(self):
    method modify_slave_count (line 175) | def modify_slave_count(self, num_slaves=1):
  class EmrException (line 179) | class EmrException(Exception):
    method __init__ (line 180) | def __init__(self, msg):
    method __str__ (line 183) | def __str__(self):

FILE: r2/r2/lib/errors.py
  function add_error_codes (line 197) | def add_error_codes(new_codes):
  class RedditError (line 209) | class RedditError(Exception):
    method __init__ (line 214) | def __init__(self, name=None, msg_params=None, fields=None, code=None):
    method message (line 231) | def message(self):
    method __iter__ (line 234) | def __iter__(self):
    method __repr__ (line 238) | def __repr__(self):
    method __str__ (line 241) | def __str__(self):
  class ErrorSet (line 245) | class ErrorSet(object):
    method __init__ (line 246) | def __init__(self):
    method __contains__ (line 249) | def __contains__(self, pair):
    method get (line 254) | def get(self, name, default=None):
    method get_first (line 257) | def get_first(self, field_name, *error_names):
    method __getitem__ (line 265) | def __getitem__(self, name):
    method __repr__ (line 268) | def __repr__(self):
    method __iter__ (line 271) | def __iter__(self):
    method __len__ (line 275) | def __len__(self):
    method add (line 278) | def add(self, error_name, msg_params=None, field=None, code=None):
    method add_error (line 284) | def add_error(self, error):
    method remove (line 288) | def remove(self, pair):
  class ForbiddenError (line 295) | class ForbiddenError(HTTPForbidden):
    method __init__ (line 296) | def __init__(self, error_name):
  class BadRequestError (line 301) | class BadRequestError(HTTPBadRequest):
    method __init__ (line 302) | def __init__(self, error_name):
  function reddit_http_error (line 311) | def reddit_http_error(code=400, error_name='UNKNOWN_ERROR', **data):
  class UserRequiredException (line 326) | class UserRequiredException(RedditError):
  class VerifiedUserRequiredException (line 331) | class VerifiedUserRequiredException(RedditError):
  class MessageError (line 336) | class MessageError(Exception): pass

FILE: r2/r2/lib/eventcollector.py
  function _make_http_date (line 57) | def _make_http_date(when=None):
  function parse_agent (line 67) | def parse_agent(ua):
  class EventQueue (line 83) | class EventQueue(object):
    method __init__ (line 84) | def __init__(self, queue=amqp):
    method save_event (line 87) | def save_event(self, event):
    method vote_event (line 103) | def vote_event(self, vote):
    method submit_event (line 161) | def submit_event(self, new_post, request=None, context=None):
    method comment_event (line 197) | def comment_event(self, new_comment, request=None, context=None):
    method cache_poisoning_event (line 243) | def cache_poisoning_event(self, poison_info, request=None, context=None):
    method muted_forbidden_event (line 304) | def muted_forbidden_event(self, details_text, subreddit=None,
    method timeout_forbidden_event (line 335) | def timeout_forbidden_event(self, action_name, details_text,
    method mod_event (line 431) | def mod_event(self, modaction, subreddit, mod, target=None,
    method report_event (line 468) | def report_event(self, reason=None, details_text=None,
    method quarantine_event (line 506) | def quarantine_event(self, event_type, subreddit,
    method modmail_event (line 552) | def modmail_event(self, message, request=None, context=None):
    method message_event (line 627) | def message_event(self, message, event_type="ss.send_message",
    method loid_event (line 692) | def loid_event(self, loid, action_name, request=None, context=None):
    method login_event (line 709) | def login_event(self, action_name, error_msg,
    method bucketing_event (line 758) | def bucketing_event(
    method page_bucketing_event (line 784) | def page_bucketing_event(
  class Event (line 811) | class Event(object):
    method __init__ (line 812) | def __init__(self, topic, event_type,
    method add (line 862) | def add(self, field, value, obfuscate=False):
    method add_text (line 874) | def add_text(self, key, value, obfuscate=False):
    method add_target_fields (line 879) | def add_target_fields(self, target):
    method add_subreddit_fields (line 930) | def add_subreddit_fields(self, subreddit):
    method get (line 937) | def get(self, field, obfuscated=False):
    method get_context_data (line 944) | def get_context_data(self, request, context):
    method get_sensitive_context_data (line 987) | def get_sensitive_context_data(self, request, context):
    method dump (line 1002) | def dump(self):
  class PublishableEvent (line 1017) | class PublishableEvent(object):
    method __init__ (line 1018) | def __init__(self, data, truncatable_field=None):
    method __len__ (line 1022) | def __len__(self):
    method truncate_data (line 1025) | def truncate_data(self, target_len):
  class EventPublisher (line 1051) | class EventPublisher(object):
    method __init__ (line 1059) | def __init__(self, url, signature_key, secret, user_agent, stats,
    method _make_signature (line 1073) | def _make_signature(self, payload):
    method _publish (line 1077) | def _publish(self, events):
    method _chunk_events (line 1104) | def _chunk_events(self, events):
    method publish (line 1145) | def publish(self, events):
  function _get_reason (line 1154) | def _get_reason(response):
  function process_events (line 1159) | def process_events(g, timeout=5.0, **kw):

FILE: r2/r2/lib/export.py
  class ExportError (line 28) | class ExportError(Exception):
    method __init__ (line 29) | def __init__(self, module):
  function export (line 36) | def export(exported_entity):

FILE: r2/r2/lib/filters.py
  function python_websafe (line 53) | def python_websafe(text):
  function python_websafe_json (line 56) | def python_websafe_json(text):
  function spaceCompress (line 62) | def spaceCompress(text):
  function spaceCompress (line 75) | def spaceCompress(content):
  class _Unsafe (line 94) | class _Unsafe(unicode):
    method cache_key (line 96) | def cache_key(self, style):
  function unsafe (line 100) | def unsafe(text=''):
  function websafe_json (line 103) | def websafe_json(text=""):
  function double_websafe (line 106) | def double_websafe(text=""):
  function conditional_websafe (line 110) | def conditional_websafe(text = ''):
  function mako_websafe (line 126) | def mako_websafe(text=''):
  function websafe (line 131) | def websafe(text=''):
  function jssafe (line 156) | def jssafe(text=u''):
  function scriptsafe_dumps (line 171) | def scriptsafe_dumps(obj, **kwargs):
  function markdown_souptest (line 192) | def markdown_souptest(text, nofollow=False, target=None, renderer='reddi...
  function safemarkdown (line 205) | def safemarkdown(text, nofollow=False, wrap=True, **kwargs):
  function wikimarkdown (line 217) | def wikimarkdown(text, include_toc=True, target=None):
  function generate_table_of_contents (line 270) | def generate_table_of_contents(soup, prefix):
  function keep_space (line 330) | def keep_space(text):
  function unkeep_space (line 337) | def unkeep_space(text):

FILE: r2/r2/lib/generate_strings.py
  function generate_strings (line 30) | def generate_strings():

FILE: r2/r2/lib/geoip.py
  function _location_by_ips (line 44) | def _location_by_ips(ips):
  function _organization_by_ips (line 69) | def _organization_by_ips(ips):
  function location_by_ips (line 91) | def location_by_ips(ips):
  function organization_by_ips (line 107) | def organization_by_ips(ips):
  function get_request_location (line 123) | def get_request_location(request, context):

FILE: r2/r2/lib/gzipper.py
  class GzipMiddleware (line 43) | class GzipMiddleware(object):
    method __init__ (line 58) | def __init__(self, app, compression_level, min_size):
    method _start_response (line 63) | def _start_response(self, status, response_headers, exc_info=None):
    method _write_not_implemented (line 70) | def _write_not_implemented(*args, **kwargs):
    method content_length (line 79) | def content_length(headers, app_iter):
    method should_gzip_response (line 102) | def should_gzip_response(self, headers, app_iter):
    method update_vary_header (line 129) | def update_vary_header(headers):
    method request_accepts_gzip (line 146) | def request_accepts_gzip(environ):
    method __call__ (line 150) | def __call__(self, environ, start_response):
  function make_gzip_middleware (line 188) | def make_gzip_middleware(app, global_conf=None, compress_level=9, min_si...

FILE: r2/r2/lib/hadoop_decompress.py
  class HadoopStreamDecompressor (line 28) | class HadoopStreamDecompressor(object):
    method __init__ (line 42) | def __init__(self):
    method decompress (line 48) | def decompress(self, data):
    method _decompress_block (line 61) | def _decompress_block(self):
    method _decompress_subblock (line 81) | def _decompress_subblock(self):
    method flush (line 97) | def flush(self):
    method copy (line 102) | def copy(self):
  function hadoop_decompress (line 111) | def hadoop_decompress(src, dst, blocksize=snappy._STREAM_TO_STREAM_BLOCK...

FILE: r2/r2/lib/hardcachebackend.py
  function expiration_from_time (line 35) | def expiration_from_time(time):
  class HardCacheBackend (line 40) | class HardCacheBackend(object):
    method __init__ (line 41) | def __init__(self, gc):
    method engine_by_category (line 91) | def engine_by_category(self, category, type="master"):
    method profile_start (line 102) | def profile_start(self, operation, category):
    method profile_stop (line 119) | def profile_stop(self, t):
    method set (line 141) | def set(self, category, ids, val, time):
    method add (line 163) | def add(self, category, ids, val, time=0):
    method incr (line 189) | def incr(self, category, ids, time=0, delta=1):
    method get (line 225) | def get(self, category, ids, force_write_table=False):
    method get_multi (line 252) | def get_multi(self, category, idses):
    method delete (line 277) | def delete(self, category, ids):
    method ids_by_category (line 285) | def ids_by_category(self, category, limit=1000):
    method clause_from_expiration (line 296) | def clause_from_expiration(self, engine, expiration):
    method expired (line 304) | def expired(self, engine, expiration_clause, limit=1000):
    method delete_if_expired (line 315) | def delete_if_expired(self, category, ids, expiration="now"):
  function delete_expired (line 325) | def delete_expired(expiration="now", limit=5000):

FILE: r2/r2/lib/hooks.py
  function all_hooks (line 53) | def all_hooks():
  class Hook (line 58) | class Hook(object):
    method __init__ (line 60) | def __init__(self):
    method register_handler (line 63) | def register_handler(self, handler):
    method call (line 67) | def call(self, **kwargs):
    method call_until_return (line 76) | def call_until_return(self, **kwargs):
  function get_hook (line 90) | def get_hook(name):
  class HookRegistrar (line 97) | class HookRegistrar(object):
    method __init__ (line 104) | def __init__(self):
    method on (line 108) | def on(self, name):
    method register_all (line 121) | def register_all(self):

FILE: r2/r2/lib/inventory.py
  function update_prediction_data (line 62) | def update_prediction_data():
  function _min_daily_pageviews_by_sr (line 77) | def _min_daily_pageviews_by_sr(ndays=NDAYS_TO_QUERY, end_date=None):
  function get_date_range (line 101) | def get_date_range(start, end):
  function get_campaigns_by_date (line 107) | def get_campaigns_by_date(srs, start, end, ignore=None):
  function get_predicted_pageviews (line 152) | def get_predicted_pageviews(srs, location=None):
  function make_target_name (line 198) | def make_target_name(target):
  function find_campaigns (line 204) | def find_campaigns(srs, start, end, ignore):
  function get_available_pageviews (line 224) | def get_available_pageviews(targets, start, end, location=None, datestr=...
  function get_oversold (line 320) | def get_oversold(target, start, end, daily_request, ignore=None, locatio...

FILE: r2/r2/lib/inventory_optimization.py
  class SimpleCampaign (line 27) | class SimpleCampaign(object):
    method __init__ (line 28) | def __init__(self, name, target_names, impressions):
    method __repr__ (line 33) | def __repr__(self):
  class SimpleTarget (line 39) | class SimpleTarget(object):
    method __init__ (line 40) | def __init__(self, name, impressions):
    method __repr__ (line 44) | def __repr__(self):
  class System (line 49) | class System(object):
    method __init__ (line 56) | def __init__(self, campaigns, targets, priority_target_names):
    method __repr__ (line 60) | def __repr__(self):
    method combine_campaigns (line 67) | def combine_campaigns(self, campaigns):
    method reduce_campaigns (line 88) | def reduce_campaigns(self, campaigns, targets):
    method reduce_targets (line 112) | def reduce_targets(self, campaigns, targets):
    method simplify (line 169) | def simplify(self, campaigns, targets):
    method get_free_impressions (line 187) | def get_free_impressions(self):
  function campaign_to_simple_campaign (line 295) | def campaign_to_simple_campaign(campaign):
  function get_maximized_pageviews (line 302) | def get_maximized_pageviews(priority_sr_names, booked_by_target,
  function run_tests (line 314) | def run_tests():

FILE: r2/r2/lib/ip_events.py
  function ips_by_account_id (line 8) | def ips_by_account_id(account_id, limit=None):
  function account_ids_by_ip (line 29) | def account_ids_by_ip(ip, after=None, before=None, limit=1000):

FILE: r2/r2/lib/js.py
  class Uglify (line 50) | class Uglify(object):
    method compile (line 51) | def compile(self, data, dest):
  class Source (line 64) | class Source(object):
    method get_source (line 66) | def get_source(self, **kwargs):
    method use (line 70) | def use(self, **kwargs):
    method dependencies (line 75) | def dependencies(self):
    method outputs (line 79) | def outputs(self):
  class FileSource (line 83) | class FileSource(Source):
    method __init__ (line 85) | def __init__(self, name):
    method __eq__ (line 88) | def __eq__(self, other):
    method get_source (line 91) | def get_source(self, use_built_statics=False):
    method url (line 105) | def url(self, absolute=False, mangle_name=False):
    method use (line 113) | def use(self, **kwargs):
    method dependencies (line 117) | def dependencies(self):
  class Module (line 123) | class Module(Source):
    method __init__ (line 125) | def __init__(self, name, *sources, **kwargs):
    method get_default_source (line 143) | def get_default_source(self, source):
    method get_flattened_sources (line 146) | def get_flattened_sources(self, flattened_sources):
    method get_source (line 159) | def get_source(self, use_built_statics=False):
    method extend (line 166) | def extend(self, module):
    method destination_path (line 170) | def destination_path(self):
    method build (line 174) | def build(self, minifier):
    method url (line 188) | def url(self, absolute=False, mangle_name=True):
    method use (line 195) | def use(self, **kwargs):
    method dependencies (line 203) | def dependencies(self):
    method outputs (line 210) | def outputs(self):
  class DataSource (line 214) | class DataSource(Source):
    method __init__ (line 216) | def __init__(self, wrap, data=None):
    method get_content (line 220) | def get_content(self, **kw):
    method get_source (line 223) | def get_source(self, use_built_statics=False):
    method use (line 228) | def use(self):
    method dependencies (line 235) | def dependencies(self):
  class PermissionsDataSource (line 239) | class PermissionsDataSource(DataSource):
    method __init__ (line 242) | def __init__(self, permission_sets):
    method _make_marked_json (line 246) | def _make_marked_json(cls, obj):
    method get_source (line 264) | def get_source(self, **kw):
    method dependencies (line 271) | def dependencies(self):
  class TemplateFileSource (line 278) | class TemplateFileSource(DataSource, FileSource):
    method __init__ (line 280) | def __init__(self, name, wrap="r.templates.set({content})"):
    method get_content (line 285) | def get_content(self, use_built_statics=False):
  class LocaleSpecificSource (line 302) | class LocaleSpecificSource(object):
    method get_localized_source (line 303) | def get_localized_source(self, lang):
  class StringsSource (line 307) | class StringsSource(LocaleSpecificSource):
    method __init__ (line 310) | def __init__(self, keys):
    method _check_formatting_specifiers (line 314) | def _check_formatting_specifiers(self, string):
    method get_localized_source (line 321) | def get_localized_source(self, lang):
  class PluralForms (line 343) | class PluralForms(LocaleSpecificSource):
    method get_localized_source (line 344) | def get_localized_source(self, lang):
  class LocalizedModule (line 350) | class LocalizedModule(Module):
    method __init__ (line 359) | def __init__(self, *args, **kwargs):
    method languagize_path (line 368) | def languagize_path(path, lang):
    method build (line 372) | def build(self, minifier):
    method use (line 399) | def use(self, **kwargs):
    method outputs (line 425) | def outputs(self):
  function src (line 741) | def src(*names, **kwargs):
  function use (line 758) | def use(*names, **kwargs):
  function load_plugin_modules (line 762) | def load_plugin_modules(plugins=None):
  function build_command (line 770) | def build_command(fn):
  function enumerate_modules (line 779) | def enumerate_modules():
  function dependencies (line 785) | def dependencies(name):
  function enumerate_outputs (line 791) | def enumerate_outputs(*names):
  function build_module (line 803) | def build_module(name):

FILE: r2/r2/lib/jsonresponse.py
  class JsonResponse (line 37) | class JsonResponse(object):
    method __init__ (line 46) | def __init__(self):
    method _clear (line 49) | def _clear(self):
    method send_failure (line 55) | def send_failure(self, error):
    method __call__ (line 60) | def __call__(self, *a, **kw):
    method __getattr__ (line 63) | def __getattr__(self, key):
    method make_response (line 66) | def make_response(self):
    method set_error (line 77) | def set_error(self, error_name, field_name):
    method has_error (line 80) | def has_error(self):
    method has_errors (line 83) | def has_errors(self, field_name, *errors, **kw):
    method process_rendered (line 93) | def process_rendered(self, res):
    method _things (line 96) | def _things(self, things, action, *a, **kw):
    method insert_things (line 114) | def insert_things(self, things, append = False, **kw):
    method replace_things (line 117) | def replace_things(self, things, keep_children = False,
    method _send_data (line 122) | def _send_data(self, **kw):
    method new_captcha (line 125) | def new_captcha(self):
    method ratelimit (line 128) | def ratelimit(self, seconds):
  class JQueryResponse (line 132) | class JQueryResponse(JsonResponse):
    method __init__ (line 144) | def __init__(self, top_node = None):
    method _clear (line 152) | def _clear(self):
    method process_rendered (line 161) | def process_rendered(self, res):
    method send_failure (line 167) | def send_failure(self, error):
    method __call__ (line 173) | def __call__(self, *a):
    method __getattr__ (line 176) | def __getattr__(self, key):
    method transform (line 180) | def transform(self, obj, op, args):
    method set_error (line 186) | def set_error(self, error_name, field_name):
    method has_error (line 192) | def has_error(self):
    method make_response (line 195) | def make_response(self):
    method _things (line 209) | def _things(self, things, action, *a, **kw):
    method insert_table_rows (line 214) | def insert_table_rows(self, rows, index = -1):
    method new_captcha (line 227) | def new_captcha(self):
    method get_input (line 232) | def get_input(self, name):
    method set_inputs (line 235) | def set_inputs(self, **kw):
    method focus_input (line 242) | def focus_input(self, name):
    method set_html (line 245) | def set_html(self, selector, value):
    method set_text (line 250) | def set_text(self, selector, value):
    method set (line 255) | def set(self, **kw):
    method refresh (line 261) | def refresh(self):

FILE: r2/r2/lib/jsontemplates.py
  function make_typename (line 52) | def make_typename(typ):
  function make_fullname (line 55) | def make_fullname(typ, _id):
  class ObjectTemplate (line 59) | class ObjectTemplate(StringTemplate):
    method __init__ (line 60) | def __init__(self, d):
    method update (line 63) | def update(self, kw):
    method finalize (line 78) | def finalize(self, kw = {}):
  class JsonTemplate (line 82) | class JsonTemplate(Template):
    method __init__ (line 83) | def __init__(self): pass
    method render (line 85) | def render(self, thing = None, *a, **kw):
  class TakedownJsonTemplate (line 89) | class TakedownJsonTemplate(JsonTemplate):
    method render (line 90) | def render(self, thing = None, *a, **kw):
  class ThingTemplate (line 94) | class ThingTemplate(object):
    method render (line 96) | def render(cls, thing):
    method get_kind (line 125) | def get_kind(cls, item):
    method get_json (line 130) | def get_json(cls, item):
    method get_rendered (line 141) | def get_rendered(cls, item, render_style):
  class ThingJsonTemplate (line 149) | class ThingJsonTemplate(JsonTemplate):
    method data_attrs (line 158) | def data_attrs(cls, **kw):
    method kind (line 163) | def kind(self, wrapped):
    method rendered_data (line 171) | def rendered_data(self, thing):
    method raw_data (line 189) | def raw_data(self, thing):
    method thing_attr (line 203) | def thing_attr(self, thing, attr):
    method data (line 235) | def data(self, thing):
    method render (line 241) | def render(self, thing = None, action = None, *a, **kw):
  class SubredditJsonTemplate (line 245) | class SubredditJsonTemplate(ThingJsonTemplate):
    method raw_data (line 313) | def raw_data(self, thing):
    method thing_attr (line 329) | def thing_attr(self, thing, attr):
  class LabeledMultiDescriptionJsonTemplate (line 377) | class LabeledMultiDescriptionJsonTemplate(ThingJsonTemplate):
    method kind (line 383) | def kind(self, wrapped):
    method thing_attr (line 386) | def thing_attr(self, thing, attr):
  class LabeledMultiJsonTemplate (line 395) | class LabeledMultiJsonTemplate(LabeledMultiDescriptionJsonTemplate):
    method __init__ (line 413) | def __init__(self, expand_srs=False):
    method kind (line 417) | def kind(self, wrapped):
    method sr_props (line 421) | def sr_props(cls, thing, srs, expand=False):
    method thing_attr (line 429) | def thing_attr(self, thing, attr):
  function get_trimmed_sr_dicts (line 446) | def get_trimmed_sr_dicts(srs, user):
  class IdentityJsonTemplate (line 493) | class IdentityJsonTemplate(ThingJsonTemplate):
    method raw_data (line 517) | def raw_data(self, thing):
    method add_message_data (line 548) | def add_message_data(self, data, thing):
    method thing_attr (line 553) | def thing_attr(self, thing, attr):
  class AccountJsonTemplate (line 583) | class AccountJsonTemplate(IdentityJsonTemplate):
    method thing_attr (line 590) | def thing_attr(self, thing, attr):
  class PrefsJsonTemplate (line 599) | class PrefsJsonTemplate(ThingJsonTemplate):
    method __init__ (line 603) | def __init__(self, fields=None):
    method thing_attr (line 612) | def thing_attr(self, thing, attr):
  function get_mod_attributes (line 618) | def get_mod_attributes(item):
  function get_author_attributes (line 642) | def get_author_attributes(item):
  function get_distinguished_attributes (line 666) | def get_distinguished_attributes(item):
  function get_edited_attributes (line 673) | def get_edited_attributes(item):
  function get_report_reason_attributes (line 683) | def get_report_reason_attributes(item):
  function get_removal_reason_attributes (line 697) | def get_removal_reason_attributes(item):
  function get_media_embed_attributes (line 706) | def get_media_embed_attributes(item):
  function get_selftext_attributes (line 738) | def get_selftext_attributes(item):
  function generate_image_links (line 749) | def generate_image_links(preview_object, file_type=None, censor_nsfw=Fal...
  class LinkJsonTemplate (line 797) | class LinkJsonTemplate(ThingTemplate):
    method get_json (line 799) | def get_json(cls, item):
    method get_rendered (line 882) | def get_rendered(cls, item, render_style):
  class PromotedLinkJsonTemplate (line 890) | class PromotedLinkJsonTemplate(LinkJsonTemplate):
    method get_json (line 892) | def get_json(cls, item):
  class CommentJsonTemplate (line 911) | class CommentJsonTemplate(ThingTemplate):
    method get_parent_id (line 913) | def get_parent_id(cls, item):
    method get_link_name (line 922) | def get_link_name(cls, item):
    method render_child (line 927) | def render_child(cls, item):
    method get_json (line 935) | def get_json(cls, item):
    method get_rendered (line 989) | def get_rendered(cls, item, render_style):
  class MoreCommentJsonTemplate (line 1001) | class MoreCommentJsonTemplate(ThingTemplate):
    method get_kind (line 1003) | def get_kind(cls, item):
    method get_json (line 1007) | def get_json(cls, item):
    method get_rendered (line 1018) | def get_rendered(cls, item, render_style):
  class MessageJsonTemplate (line 1030) | class MessageJsonTemplate(ThingJsonTemplate):
    method thing_attr (line 1049) | def thing_attr(self, thing, attr):
    method raw_data (line 1082) | def raw_data(self, thing):
    method rendered_data (line 1089) | def rendered_data(self, wrapped):
  class RedditJsonTemplate (line 1101) | class RedditJsonTemplate(JsonTemplate):
    method render (line 1102) | def render(self, thing = None, *a, **kw):
  class PanestackJsonTemplate (line 1105) | class PanestackJsonTemplate(JsonTemplate):
    method render (line 1106) | def render(self, thing = None, *a, **kw):
  class NullJsonTemplate (line 1113) | class NullJsonTemplate(JsonTemplate):
    method render (line 1114) | def render(self, thing = None, *a, **kw):
    method get_def (line 1117) | def get_def(self, name):
  class ListingJsonTemplate (line 1120) | class ListingJsonTemplate(ThingJsonTemplate):
    method thing_attr (line 1128) | def thing_attr(self, thing, attr):
    method rendered_data (line 1141) | def rendered_data(self, thing):
    method kind (line 1144) | def kind(self, wrapped):
  class SearchListingJsonTemplate (line 1148) | class SearchListingJsonTemplate(ListingJsonTemplate):
    method raw_data (line 1149) | def raw_data(self, thing):
  class UserListingJsonTemplate (line 1164) | class UserListingJsonTemplate(ListingJsonTemplate):
    method raw_data (line 1165) | def raw_data(self, thing):
    method kind (line 1170) | def kind(self, wrapped):
  class UserListJsonTemplate (line 1173) | class UserListJsonTemplate(ThingJsonTemplate):
    method thing_attr (line 1178) | def thing_attr(self, thing, attr):
    method rendered_data (line 1187) | def rendered_data(self, thing):
    method kind (line 1190) | def kind(self, wrapped):
  class UserTableItemJsonTemplate (line 1194) | class UserTableItemJsonTemplate(ThingJsonTemplate):
    method thing_attr (line 1200) | def thing_attr(self, thing, attr):
    method render (line 1203) | def render(self, thing, *a, **kw):
  class RelTableItemJsonTemplate (line 1207) | class RelTableItemJsonTemplate(UserTableItemJsonTemplate):
    method thing_attr (line 1212) | def thing_attr(self, thing, attr):
  class FriendTableItemJsonTemplate (line 1228) | class FriendTableItemJsonTemplate(RelTableItemJsonTemplate):
    method inject_data (line 1229) | def inject_data(self, thing, d):
    method rendered_data (line 1234) | def rendered_data(self, thing):
    method raw_data (line 1238) | def raw_data(self, thing):
  class BannedTableItemJsonTemplate (line 1243) | class BannedTableItemJsonTemplate(RelTableItemJsonTemplate):
  class MutedTableItemJsonTemplate (line 1249) | class MutedTableItemJsonTemplate(RelTableItemJsonTemplate):
  class InvitedModTableItemJsonTemplate (line 1253) | class InvitedModTableItemJsonTemplate(RelTableItemJsonTemplate):
    method thing_attr (line 1258) | def thing_attr(self, thing, attr):
  class OrganicListingJsonTemplate (line 1266) | class OrganicListingJsonTemplate(ListingJsonTemplate):
    method kind (line 1267) | def kind(self, wrapped):
  class TrafficJsonTemplate (line 1270) | class TrafficJsonTemplate(JsonTemplate):
    method render (line 1271) | def render(self, thing, *a, **kw):
  class WikiJsonTemplate (line 1285) | class WikiJsonTemplate(JsonTemplate):
    method render (line 1286) | def render(self, thing, *a, **kw):
  class WikiPageListingJsonTemplate (line 1293) | class WikiPageListingJsonTemplate(ThingJsonTemplate):
    method kind (line 1294) | def kind(self, thing):
    method data (line 1297) | def data(self, thing):
  class WikiViewJsonTemplate (line 1301) | class WikiViewJsonTemplate(ThingJsonTemplate):
    method kind (line 1302) | def kind(self, thing):
    method data (line 1305) | def data(self, thing):
  class WikiSettingsJsonTemplate (line 1316) | class WikiSettingsJsonTemplate(ThingJsonTemplate):
    method kind (line 1317) | def kind(self, thing):
    method data (line 1320) | def data(self, thing):
  class WikiRevisionJsonTemplate (line 1326) | class WikiRevisionJsonTemplate(ThingJsonTemplate):
    method render (line 1327) | def render(self, thing, *a, **kw):
  class FlairListJsonTemplate (line 1340) | class FlairListJsonTemplate(JsonTemplate):
    method render (line 1341) | def render(self, thing, *a, **kw):
  class FlairCsvJsonTemplate (line 1360) | class FlairCsvJsonTemplate(JsonTemplate):
    method render (line 1361) | def render(self, thing, *a, **kw):
  class FlairSelectorJsonTemplate (line 1365) | class FlairSelectorJsonTemplate(JsonTemplate):
    method _template_dict (line 1366) | def _template_dict(self, flair):
    method render (line 1373) | def render(self, thing, *a, **kw):
  class StylesheetTemplate (line 1421) | class StylesheetTemplate(ThingJsonTemplate):
    method kind (line 1428) | def kind(self, wrapped):
    method images (line 1431) | def images(self):
    method thing_attr (line 1440) | def thing_attr(self, thing, attr):
  class SubredditSettingsTemplate (line 1447) | class SubredditSettingsTemplate(ThingJsonTemplate):
    method kind (line 1482) | def kind(self, wrapped):
    method thing_attr (line 1485) | def thing_attr(self, thing, attr):
    method raw_data (line 1493) | def raw_data(self, thing):
  class UploadedImageJsonTemplate (line 1505) | class UploadedImageJsonTemplate(JsonTemplate):
    method render (line 1506) | def render(self, thing, *a, **kw):
  class ModActionTemplate (line 1513) | class ModActionTemplate(ThingJsonTemplate):
    method thing_attr (line 1531) | def thing_attr(self, thing, attr):
    method kind (line 1560) | def kind(self, wrapped):
  class PolicyViewJsonTemplate (line 1564) | class PolicyViewJsonTemplate(ThingJsonTemplate):
    method kind (line 1572) | def kind(self, wrapped):
  class KarmaListJsonTemplate (line 1575) | class KarmaListJsonTemplate(ThingJsonTemplate):
    method data (line 1576) | def data(self, karmas):
    method kind (line 1586) | def kind(self, wrapped):
  function get_usertrophies (line 1590) | def get_usertrophies(user):
  class TrophyJsonTemplate (line 1599) | class TrophyJsonTemplate(ThingJsonTemplate):
    method thing_attr (line 1610) | def thing_attr(self, thing, attr):
    method kind (line 1621) | def kind(self, thing):
  class TrophyListJsonTemplate (line 1624) | class TrophyListJsonTemplate(ThingJsonTemplate):
    method data (line 1625) | def data(self, trophies):
    method kind (line 1629) | def kind(self, wrapped):
  class RulesJsonTemplate (line 1633) | class RulesJsonTemplate(JsonTemplate):
    method render (line 1634) | def render(self, thing, *a, **kw):

FILE: r2/r2/lib/language.py
  function charset_name (line 9) | def charset_name(name, start, end):
  function symbology (line 171) | def symbology(s):
  function charset_summary (line 188) | def charset_summary(s, prefix=""):

FILE: r2/r2/lib/lock.py
  class TimeoutExpired (line 42) | class TimeoutExpired(Exception): pass
  class MemcacheLock (line 44) | class MemcacheLock(object):
    method __init__ (line 49) | def __init__(self, stats, group, key, cache,
    method __enter__ (line 64) | def __enter__(self):
    method __exit__ (line 68) | def __exit__(self, type, value, tb):
    method acquire (line 71) | def acquire(self):
    method release (line 125) | def release(self):
  function make_lock_factory (line 140) | def make_lock_factory(cache, stats):

FILE: r2/r2/lib/log.py
  function get_operational_exceptions (line 37) | def get_operational_exceptions():
  class SanitizeStackLocalsProcessor (line 58) | class SanitizeStackLocalsProcessor(Processor):
    method filter_stacktrace (line 71) | def filter_stacktrace(self, data, **kwargs):
  class RavenErrorReporter (line 98) | class RavenErrorReporter(Reporter):
    method get_module_versions (line 100) | def get_module_versions(cls):
    method add_http_context (line 107) | def add_http_context(cls, client):
    method add_reddit_context (line 141) | def add_reddit_context(cls, client):
    method add_user_context (line 153) | def add_user_context(cls, client):
    method get_raven_client (line 166) | def get_raven_client(cls):
    method capture_exception (line 195) | def capture_exception(cls, exc_info=None):
    method report (line 235) | def report(self, exc_data):
  function write_error_summary (line 239) | def write_error_summary(error):
  class LoggingErrorReporter (line 246) | class LoggingErrorReporter(Reporter):
    method report (line 249) | def report(self, exc_data):

FILE: r2/r2/lib/loid.py
  function isodate (line 22) | def isodate(d):
  function ensure_unquoted (line 30) | def ensure_unquoted(cookie_str):
  class LoId (line 49) | class LoId(object):
    method __init__ (line 59) | def __init__(
    method _trigger_event (line 86) | def _trigger_event(self, action):
    method _create (line 95) | def _create(cls, request, context):
    method load (line 115) | def load(cls, request, context, create=True):
    method save (line 148) | def save(self, **cookie_attrs):
    method to_dict (line 163) | def to_dict(self, prefix=None):

FILE: r2/r2/lib/manager/db_manager.py
  function get_engine (line 37) | def get_engine(name, db_host='', db_user='', db_pass='', db_port='5432',
  class db_manager (line 72) | class db_manager:
    method __init__ (line 73) | def __init__(self):
    method add_thing (line 82) | def add_thing(self, name, thing_dbs, avoid_master=False, **kw):
    method add_relation (line 88) | def add_relation(self, name, type1, type2, relation_dbs,
    method setup_db (line 93) | def setup_db(self, db_name, g_override=None, **params):
    method things_iter (line 103) | def things_iter(self):
    method rels_iter (line 110) | def rels_iter(self):
    method mark_dead (line 116) | def mark_dead(self, engine, g_override=None):
    method test_engine (line 120) | def test_engine(self, engine, g_override=None):
    method get_engine (line 134) | def get_engine(self, name):
    method get_engines (line 137) | def get_engines(self, names):
    method get_read_table (line 140) | def get_read_table(self, tables):

FILE: r2/r2/lib/manager/tp_manager.py
  class tp_manager (line 35) | class tp_manager:
    method __init__ (line 36) | def __init__(self, template_cls=mTemplate):
    method add_handler (line 41) | def add_handler(self, name, style, handler):
    method cache_template (line 49) | def cache_template(self, cls, style, template):
    method get_template (line 59) | def get_template(self, cls, style):
    method get (line 79) | def get(self, thing, style):

FILE: r2/r2/lib/media.py
  function _image_to_str (line 80) | def _image_to_str(image):
  function str_to_image (line 86) | def str_to_image(s):
  function _image_entropy (line 92) | def _image_entropy(img):
  function _crop_image_vertically (line 101) | def _crop_image_vertically(img, target_height):
  function _square_image (line 124) | def _square_image(img):
  function _apply_exif_orientation (line 130) | def _apply_exif_orientation(image):
  function _prepare_image (line 172) | def _prepare_image(image):
  function _clean_url (line 192) | def _clean_url(url):
  function _initialize_request (line 199) | def _initialize_request(url, referer, gzip=False):
  function _fetch_url (line 215) | def _fetch_url(url, referer=None):
  function _fetch_image_size (line 230) | def _fetch_image_size(url, referer):
  function optimize_jpeg (line 257) | def optimize_jpeg(filename):
  function thumbnail_url (line 262) | def thumbnail_url(link):
  function _filename_from_content (line 273) | def _filename_from_content(contents):
  function upload_media (line 278) | def upload_media(image, file_type='.jpg', category='thumbs'):
  function upload_stylesheet (line 315) | def upload_stylesheet(content):
  function _scrape_media (line 320) | def _scrape_media(url, autoplay=False, maxwidth=600, force=False,
  function _get_scrape_url (line 396) | def _get_scrape_url(link):
  function _set_media (line 426) | def _set_media(link, force=False, **kwargs):
  function force_thumbnail (line 474) | def force_thumbnail(link, image_data, file_type=".jpg"):
  function force_mobile_ad_image (line 484) | def force_mobile_ad_image(link, image_data, file_type=".jpg"):
  function upload_icon (line 498) | def upload_icon(image_data, size):
  function allowed_media_preview_url (line 507) | def allowed_media_preview_url(url):
  function get_preview_image (line 517) | def get_preview_image(preview_object, include_censored=False):
  function _make_custom_media_embed (line 574) | def _make_custom_media_embed(media_object):
  function get_media_embed (line 583) | def get_media_embed(media_object):
  class MediaEmbed (line 602) | class MediaEmbed(object):
    method __init__ (line 610) | def __init__(self, height, width, content, scrolling=False,
  class Scraper (line 634) | class Scraper(object):
    method for_url (line 636) | def for_url(cls, url, autoplay=False, maxwidth=600, use_youtube_scrape...
    method scrape (line 653) | def scrape(self):
    method media_embed (line 659) | def media_embed(cls, media_object):
  class _ThumbnailOnlyScraper (line 664) | class _ThumbnailOnlyScraper(Scraper):
    method __init__ (line 665) | def __init__(self, url):
    method scrape (line 671) | def scrape(self):
    method _extract_image_urls (line 702) | def _extract_image_urls(self, soup):
    method _find_thumbnail_image (line 706) | def _find_thumbnail_image(self):
  class _EmbedlyScraper (line 777) | class _EmbedlyScraper(Scraper):
    method __init__ (line 784) | def __init__(self, url, autoplay=False, maxwidth=600):
    method _fetch_from_embedly (line 792) | def _fetch_from_embedly(self, secure):
    method _make_media_object (line 811) | def _make_media_object(self, oembed):
    method scrape (line 819) | def scrape(self):
    method validate_secure_oembed (line 856) | def validate_secure_oembed(self, oembed):
    method media_embed (line 873) | def media_embed(cls, media_object):
  class _YouTubeScraper (line 891) | class _YouTubeScraper(Scraper):
    method __init__ (line 895) | def __init__(self, url, maxwidth):
    method matches (line 900) | def matches(cls, url):
    method _fetch_from_youtube (line 903) | def _fetch_from_youtube(self):
    method _make_media_object (line 915) | def _make_media_object(self, oembed):
    method scrape (line 923) | def scrape(self):
    method media_embed (line 955) | def media_embed(cls, media_object):
  function _fetch_embedly_service_data (line 975) | def _fetch_embedly_service_data():
  function _fetch_embedly_services (line 980) | def _fetch_embedly_services():
  function run (line 997) | def run():

FILE: r2/r2/lib/memoize.py
  function memoize (line 31) | def memoize(iden, time = 0, stale=False, timeout=30):
  function test (line 72) | def test(x, y):

FILE: r2/r2/lib/menus.py
  class MenuHandler (line 36) | class MenuHandler(StringHandler):
    method __getattr__ (line 42) | def __getattr__(self, attr):
  function menu_style (line 199) | def menu_style(type):
  class NavMenu (line 215) | class NavMenu(Styled):
    method __init__ (line 221) | def __init__(self, options, default=None, title='', type="dropdown",
    method find_selected (line 248) | def find_selected(self):
    method __iter__ (line 261) | def __iter__(self):
    method cachable_attrs (line 265) | def cachable_attrs(self):
  class NavButton (line 274) | class NavButton(Styled):
    method __init__ (line 282) | def __init__(self, title, dest, sr_path=True, aliases=None,
    method build (line 301) | def build(self, base_path=''):
    method is_selected (line 314) | def is_selected(self):
    method selected_title (line 329) | def selected_title(self):
    method cachable_attrs (line 334) | def cachable_attrs(self):
  class QueryButton (line 347) | class QueryButton(NavButton):
    method __init__ (line 348) | def __init__(self, title, dest, query_param, sr_path=True, aliases=None,
    method build (line 355) | def build(self, base_path=''):
    method is_selected (line 366) | def is_selected(self):
  class PostButton (line 372) | class PostButton(NavButton):
    method __init__ (line 375) | def __init__(self, title, dest, input_name, sr_path=True, aliases=None,
    method build (line 382) | def build(self, base_path=''):
    method cachable_attrs (line 386) | def cachable_attrs(self):
    method is_selected (line 399) | def is_selected(self):
  class ModeratorMailButton (line 403) | class ModeratorMailButton(NavButton):
    method is_selected (line 404) | def is_selected(self):
  class OffsiteButton (line 411) | class OffsiteButton(NavButton):
    method build (line 412) | def build(self, base_path=''):
    method cachable_attrs (line 416) | def cachable_attrs(self):
  class SubredditButton (line 424) | class SubredditButton(NavButton):
    method __init__ (line 435) | def __init__(self, sr, css_class='', data=None):
    method build (line 443) | def build(self, base_path=''):
    method is_selected (line 446) | def is_selected(self):
    method cachable_attrs (line 449) | def cachable_attrs(self):
  class NamedButton (line 459) | class NamedButton(NavButton):
    method __init__ (line 465) | def __init__(self, name, sr_path=True, aliases=None,
  class JsButton (line 477) | class JsButton(NavButton):
    method __init__ (line 483) | def __init__(self, title, tab_name=None, onclick='', css_class='',
    method build (line 491) | def build(self, base_path=''):
    method is_selected (line 497) | def is_selected(self):
    method cachable_attrs (line 500) | def cachable_attrs(self):
  class PageNameNav (line 513) | class PageNameNav(Styled):
  class SortMenu (line 520) | class SortMenu(NavMenu):
    method __init__ (line 531) | def __init__(self, default=None, title='', base_path='', separator='|',
    method make_buttons (line 541) | def make_buttons(self):
    method make_title (line 550) | def make_title(self, attr):
    method operator (line 566) | def operator(cls, sort):
    method sort (line 570) | def sort(cls, operator):
  class ProfileSortMenu (line 574) | class ProfileSortMenu(SortMenu):
  class CommentSortMenu (line 579) | class CommentSortMenu(SortMenu):
    method __init__ (line 590) | def __init__(self, *args, **kwargs):
    method visible_options (line 595) | def visible_options(cls):
    method make_title (line 598) | def make_title(self, attr):
  class SearchSortMenu (line 606) | class SearchSortMenu(SortMenu):
    method hidden_options (line 612) | def hidden_options(cls):
    method make_buttons (line 615) | def make_buttons(self):
  class SubredditSearchSortMenu (line 623) | class SubredditSearchSortMenu(SortMenu):
  class RecSortMenu (line 629) | class RecSortMenu(SortMenu):
  class KindMenu (line 635) | class KindMenu(SortMenu):
    method make_title (line 641) | def make_title(self, attr):
  class TimeMenu (line 647) | class TimeMenu(SortMenu):
    method operator (line 655) | def operator(self, time):
  class CommentsTimeMenu (line 660) | class CommentsTimeMenu(TimeMenu):
  class ProfileOverviewTimeMenu (line 665) | class ProfileOverviewTimeMenu(TimeMenu):
  class ControversyTimeMenu (line 670) | class ControversyTimeMenu(TimeMenu):
  class SubredditMenu (line 676) | class SubredditMenu(NavMenu):
    method find_selected (line 677) | def find_selected(self):
  class JsNavMenu (line 682) | class JsNavMenu(NavMenu):
    method find_selected (line 683) | def find_selected(self):
  class AdminReporterMenu (line 689) | class AdminReporterMenu(SortMenu):
  class AdminKindMenu (line 693) | class AdminKindMenu(KindMenu):
  class AdminTimeMenu (line 697) | class AdminTimeMenu(TimeMenu):

FILE: r2/r2/lib/merge.py
  class ConflictException (line 33) | class ConflictException(Exception):
    method __init__ (line 34) | def __init__(self, new, your, original):
  function make_htmldiff (line 42) | def make_htmldiff(a, b, adesc, bdesc):
  function threewaymerge (line 55) | def threewaymerge(original, a, b):
  class test_globals (line 75) | class test_globals:

FILE: r2/r2/lib/message_to_email.py
  function get_reply_to_address (line 41) | def get_reply_to_address(message):
  function parse_and_validate_reply_to_address (line 75) | def parse_and_validate_reply_to_address(address):
  function get_message_subject (line 105) | def get_message_subject(message):
  function get_email_ids (line 118) | def get_email_ids(message):
  function get_system_from_address (line 135) | def get_system_from_address(sr):
  function send_modmail_email (line 140) | def send_modmail_email(message):
  function send_blocked_muted_email (line 207) | def send_blocked_muted_email(sr, parent, sender_email, incoming_email_id):
  function queue_modmail_email (line 225) | def queue_modmail_email(message):
  function queue_blocked_muted_email (line 235) | def queue_blocked_muted_email(sr, parent, sender_email, incoming_email_id):
  function process_modmail_email (line 248) | def process_modmail_email():

FILE: r2/r2/lib/migrate/campaigns_to_things.py
  function fix_trans_id (line 27) | def fix_trans_id():

FILE: r2/r2/lib/migrate/migrate.py
  function add_allow_top_to_srs (line 28) | def add_allow_top_to_srs():
  function subscribe_to_blog_and_annoucements (line 39) | def subscribe_to_blog_and_annoucements(filename):
  function recompute_unread (line 64) | def recompute_unread(min_date = None):
  function pushup_permacache (line 97) | def pushup_permacache(verbosity=1000):
  function port_cassaurls (line 188) | def port_cassaurls(after_id=None, estimate=15231317):
  function port_deleted_links (line 210) | def port_deleted_links(after_id=None):
  function convert_query_cache_to_json (line 229) | def convert_query_cache_to_json():
  function populate_spam_filtered (line 244) | def populate_spam_filtered():

FILE: r2/r2/lib/migrate/mr_domains.py
  function join_links (line 61) | def join_links():
  function time_listings (line 65) | def time_listings(times = ('all',)):
  function store_keys (line 102) | def store_keys(key, maxes):
  function write_permacache (line 153) | def write_permacache(fd = sys.stdin):

FILE: r2/r2/lib/migrate/mr_permacache.py
  function join_links (line 110) | def join_links():
  function link_listings (line 113) | def link_listings():
  function join_comments (line 145) | def join_comments():
  function comment_listings (line 148) | def comment_listings():
  function rel_listings (line 158) | def rel_listings(names, thing2_cls = Link):
  function linkvote_listings (line 168) | def linkvote_listings():
  function savehide_listings (line 172) | def savehide_listings():
  function insert_to_query (line 176) | def insert_to_query(q, items):
  function store_keys (line 179) | def store_keys(key, maxes):
  function top1k_writefiles (line 216) | def top1k_writefiles(dirname):
  function top1k_writepermacache (line 259) | def top1k_writepermacache(fd = sys.stdin):
  function write_permacache_from_dir (line 264) | def write_permacache_from_dir(dirname):
  function write_permacache_from_file (line 294) | def write_permacache_from_file(fname):

FILE: r2/r2/lib/migrate/vote_details_ip_backfill.py
  function backfill_vote_details (line 13) | def backfill_vote_details(cls):
  function main (line 52) | def main():

FILE: r2/r2/lib/mr_tools/mr_tools.py
  function join_things (line 29) | def join_things(fields, deleted=False, spam=True):
  class Mapper (line 86) | class Mapper(object):
    method __init__ (line 87) | def __init__(self):
    method process (line 90) | def process(self, values):
    method __call__ (line 93) | def __call__(self, line):
  function mr_map_parallel (line 98) | def mr_map_parallel(processor, fd = stdin,
  function test (line 116) | def test():
  class UpperMapper (line 124) | class UpperMapper(Mapper):
    method process (line 125) | def process(self, values):
  function test_parallel (line 128) | def test_parallel():

FILE: r2/r2/lib/mr_top.py
  function join_things (line 55) | def join_things(thing_type):
  function _get_cutoffs (line 59) | def _get_cutoffs(intervals):
  function time_listings (line 70) | def time_listings(intervals, thing_type):
  function store_keys (line 116) | def store_keys(key, maxes):
  function write_permacache (line 143) | def write_permacache(fd = sys.stdin):
  function reduce_listings (line 148) | def reduce_listings(fd=sys.stdin):

FILE: r2/r2/lib/newsletter.py
  class NewsletterError (line 37) | class NewsletterError(Exception):
  class EmailUnacceptableError (line 41) | class EmailUnacceptableError(NewsletterError):
  function add_subscriber (line 45) | def add_subscriber(email, source=""):

FILE: r2/r2/lib/normalized_hot.py
  function get_hot_tuples (line 38) | def get_hot_tuples(sr_ids, ageweight=None):
  function get_hot_factor (line 63) | def get_hot_factor(qdata, now, ageweight):
  function normalized_hot (line 82) | def normalized_hot(sr_ids, obey_age_limit=True, ageweight=None):

FILE: r2/r2/lib/nymph.py
  function optimize_png (line 43) | def optimize_png(filename):
  function _extract_css_info (line 48) | def _extract_css_info(match):
  class SpritableImage (line 61) | class SpritableImage(object):
    method __init__ (line 62) | def __init__(self, image, should_stretch=False):
    method width (line 68) | def width(self):
    method height (line 72) | def height(self):
    method stretch_to_width (line 75) | def stretch_to_width(self, width):
  class SpriteBin (line 79) | class SpriteBin(object):
    method __init__ (line 80) | def __init__(self, bounding_box):
    method has_space_for (line 87) | def has_space_for(self, image):
    method add_image (line 91) | def add_image(self, image):
  function _load_spritable_images (line 96) | def _load_spritable_images(css_filename):
  function _generate_sprite (line 120) | def _generate_sprite(images, sprite_path):
  function _rewrite_css (line 168) | def _rewrite_css(css_filename, sprite_path, images, sprite_size):
  function spritify (line 202) | def spritify(css_filename, sprite_path):

FILE: r2/r2/lib/organic.py
  function cached_organic_links (line 34) | def cached_organic_links(*sr_ids):
  function organic_links (line 59) | def organic_links(user):

FILE: r2/r2/lib/pages/admin_pages.py
  function admin_menu (line 39) | def admin_menu(**kwargs):
  class AdminSidebar (line 50) | class AdminSidebar(Templated):
    method __init__ (line 51) | def __init__(self, user):
  class SponsorSidebar (line 56) | class SponsorSidebar(Templated):
    method __init__ (line 57) | def __init__(self, user):
  class Details (line 62) | class Details(Templated):
    method __init__ (line 63) | def __init__(self, link, *a, **kw):
  class AdminPage (line 68) | class AdminPage(Reddit):
    method __init__ (line 74) | def __init__(self, nav_menus = None, *a, **kw):
  class AdminProfileMenu (line 77) | class AdminProfileMenu(NavMenu):
    method __init__ (line 78) | def __init__(self, path):
  class AdminLinkMenu (line 83) | class AdminLinkMenu(NavMenu):
    method __init__ (line 84) | def __init__(self, link):
  class AdminNotesSidebar (line 88) | class AdminNotesSidebar(Templated):
    method __init__ (line 103) | def __init__(self, system, subject):
  class AdminLinkInfoBar (line 116) | class AdminLinkInfoBar(LinkInfoBar):
  class AdminDetailsBar (line 120) | class AdminDetailsBar(Templated):

FILE: r2/r2/lib/pages/pages.py
  function get_captcha (line 180) | def get_captcha():
  function responsive (line 184) | def responsive(res, space_compress=None):
  class Robots (line 208) | class Robots(Templated):
    method __init__ (line 210) | def __init__(self, **context):
  class CrossDomain (line 214) | class CrossDomain(Templated):
  class Reddit (line 218) | class Reddit(Templated):
    method __init__ (line 255) | def __init__(self, space_compress=None, nav_menus=None, loginbox=True,
    method get_subreddit_stylesheet_url (line 457) | def get_subreddit_stylesheet_url(sr):
    method wiki_actions_menu (line 470) | def wiki_actions_menu(self, moderator=False):
    method sr_admin_menu (line 507) | def sr_admin_menu(self):
    method rightbox (line 634) | def rightbox(self):
    method render (line 879) | def render(self, *a, **kw):
    method corner_buttons (line 893) | def corner_buttons(self):
    method build_toolbars (line 923) | def build_toolbars(self):
    method __repr__ (line 983) | def __repr__(self):
    method content_stack (line 987) | def content_stack(panes, css_class = None):
    method content (line 991) | def content(self):
    method build_popup_panes (line 1006) | def build_popup_panes(self):
    method is_gold_page (line 1020) | def is_gold_page(self):
 
Condensed preview — 905 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (8,370K chars).
[
  {
    "path": ".drone.yml",
    "chars": 2552,
    "preview": "# This file is used by the Drone CI Server and Agents to determine what\n# should happen (if anything) in response to git"
  },
  {
    "path": ".gitignore",
    "chars": 542,
    "preview": "*~\n.*.sw?\n*.pyc\n*.pyo\n.DS_Store\n\n# mako\nr2/data/\n*.html.py\n\n# build outputs\n*.so\nbuild/\ndist/\nr2/r2.egg-info/\nr2/r2/lib/"
  },
  {
    "path": ".travis.yml",
    "chars": 363,
    "preview": "sudo: required\ndist: trusty\n\nlanguage: python\n\npython:\n    - \"2.7\"\n\nvirtualenv:\n    system_site_packages: true\n\nservices"
  },
  {
    "path": "LICENSE",
    "chars": 27222,
    "preview": "reddit Inc.\n\nCommon Public Attribution License Version 1.0 (CPAL)\n\n1. \"Definitions\"\n\n1.0.1 \"Commercial Use\" means distri"
  },
  {
    "path": "README.md",
    "chars": 1108,
    "preview": "## This repository is archived.\n\nThis repository is archived and will not receive any updates or accept issues or pull r"
  },
  {
    "path": "SECURITY.md",
    "chars": 575,
    "preview": "![white hat trophy](https://b.thumbs.redditmedia.com/n0_7BYpCg_RYB1j7.png)\n\nLike all pieces of software, reddit has bugs"
  },
  {
    "path": "Vagrantfile",
    "chars": 6774,
    "preview": "# -*- mode: ruby -*-\n# vi: set ft=ruby :\n\n# This assumes that the host machine has r2 and all the reddit plugins checked"
  },
  {
    "path": "install/README.md",
    "chars": 976,
    "preview": "# Reddit installer development instructions\n\nThis folder contains all of the installation scripts required to build redd"
  },
  {
    "path": "install/done.sh",
    "chars": 2599,
    "preview": "#!/bin/bash\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"Licens"
  },
  {
    "path": "install/drone.sh",
    "chars": 1740,
    "preview": "#!/usr/bin/env bash\n###############################################################################\n# reddit Drone envir"
  },
  {
    "path": "install/install.cfg",
    "chars": 2287,
    "preview": "#!/bin/bash\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"Licens"
  },
  {
    "path": "install/install_apt.sh",
    "chars": 3182,
    "preview": "#!/bin/bash\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"Licens"
  },
  {
    "path": "install/install_cassandra.sh",
    "chars": 1727,
    "preview": "#!/bin/bash\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"Licens"
  },
  {
    "path": "install/install_services.sh",
    "chars": 2042,
    "preview": "#!/bin/bash\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"Licens"
  },
  {
    "path": "install/install_zookeeper.sh",
    "chars": 1335,
    "preview": "#!/bin/bash\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"Licens"
  },
  {
    "path": "install/reddit.sh",
    "chars": 19878,
    "preview": "#!/bin/bash\n###############################################################################\n# reddit dev environment ins"
  },
  {
    "path": "install/setup_cassandra.sh",
    "chars": 1984,
    "preview": "#!/bin/bash\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"Licens"
  },
  {
    "path": "install/setup_mcrouter.sh",
    "chars": 3560,
    "preview": "#!/bin/bash\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"Licens"
  },
  {
    "path": "install/setup_postgres.sh",
    "chars": 3083,
    "preview": "#!/bin/bash\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"Licens"
  },
  {
    "path": "install/setup_rabbitmq.sh",
    "chars": 1727,
    "preview": "#!/bin/bash\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"Licens"
  },
  {
    "path": "install/travis.sh",
    "chars": 4588,
    "preview": "#!/bin/bash\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"Licens"
  },
  {
    "path": "install-reddit.sh",
    "chars": 3443,
    "preview": "#!/bin/bash\n###############################################################################\n# reddit dev environment ins"
  },
  {
    "path": "r2/Makefile",
    "chars": 7324,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/Makefile.py",
    "chars": 2199,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/babel.cfg",
    "chars": 1503,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/check-code",
    "chars": 13152,
    "preview": "#!/usr/bin/python\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \""
  },
  {
    "path": "r2/coverage.sh",
    "chars": 1144,
    "preview": "#!/bin/bash\nset -e\n\nBASEDIR=$(readlink -f $(dirname $0))\ncd $BASEDIR\n\nVERSION=$(git rev-parse HEAD)\nCOVERDIR=\"$BASEDIR/b"
  },
  {
    "path": "r2/pylintrc",
    "chars": 7520,
    "preview": "[MASTER]\n\n# Specify a configuration file.\n#rcfile=\n\n# Python code to execute, usually for sys.path manipulation such as\n"
  },
  {
    "path": "r2/r2/__init__.py",
    "chars": 1858,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/commands.py",
    "chars": 3618,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/config/__init__.py",
    "chars": 1124,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/config/environment.py",
    "chars": 4668,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/config/extensions.py",
    "chars": 2453,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/config/feature/README.md",
    "chars": 8069,
    "preview": "# Feature\n\n`r2.config.feature` is reddit's feature flagging API. It lets us quickly\nswitch on and off features for speci"
  },
  {
    "path": "r2/r2/config/feature/__init__.py",
    "chars": 1195,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/config/feature/feature.py",
    "chars": 4852,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/config/feature/state.py",
    "chars": 19095,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/config/feature/world.py",
    "chars": 4273,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/config/hooks.py",
    "chars": 1560,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/config/middleware.py",
    "chars": 19851,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/config/paths.py",
    "chars": 1738,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/config/queues.py",
    "chars": 5133,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/config/routing.py",
    "chars": 23210,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/config/templates.py",
    "chars": 3659,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/__init__.py",
    "chars": 4347,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/admin.py",
    "chars": 1680,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/api.py",
    "chars": 199119,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/api_docs.py",
    "chars": 9294,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/apiv1/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "r2/r2/controllers/apiv1/gold.py",
    "chars": 4155,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/apiv1/login.py",
    "chars": 3121,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/apiv1/scopes.py",
    "chars": 2441,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/apiv1/user.py",
    "chars": 7615,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/awards.py",
    "chars": 2451,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/buttons.py",
    "chars": 4709,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/captcha.py",
    "chars": 2219,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/embed.py",
    "chars": 3378,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/error.py",
    "chars": 10695,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/front.py",
    "chars": 80087,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/googletagmanager.py",
    "chars": 2091,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/health.py",
    "chars": 2899,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/ipn.py",
    "chars": 54516,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/listingcontroller.py",
    "chars": 75309,
    "preview": "# -*- coding: utf-8 -*-\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. "
  },
  {
    "path": "r2/r2/controllers/login.py",
    "chars": 6457,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/mailgun.py",
    "chars": 5882,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/mediaembed.py",
    "chars": 2829,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/multi.py",
    "chars": 15545,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/newsletter.py",
    "chars": 1331,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/oauth2.py",
    "chars": 26955,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/oembed.py",
    "chars": 7087,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/policies.py",
    "chars": 4452,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/post.py",
    "chars": 8294,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/promotecontroller.py",
    "chars": 64000,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/reddit_base.py",
    "chars": 55224,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/redirect.py",
    "chars": 2265,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/robots.py",
    "chars": 2901,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/toolbar.py",
    "chars": 4339,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/web.py",
    "chars": 7593,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/controllers/wiki.py",
    "chars": 24276,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/data/locations.json",
    "chars": 31672,
    "preview": "{\n  \"AD\": {\n    \"name\": \"Andorra\"\n  }, \n  \"AE\": {\n    \"name\": \"United Arab Emirates\"\n  }, \n  \"AF\": {\n    \"name\": \"Afghan"
  },
  {
    "path": "r2/r2/lib/__init__.py",
    "chars": 1124,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/amqp.py",
    "chars": 13545,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/app_globals.py",
    "chars": 38073,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/authorize/__init__.py",
    "chars": 1168,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/authorize/api.py",
    "chars": 22733,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/authorize/interaction.py",
    "chars": 7057,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/automoderator.py",
    "chars": 57936,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/base.py",
    "chars": 10868,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/baseplate_integration.py",
    "chars": 3760,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/butler.py",
    "chars": 4551,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/c/filters.c",
    "chars": 8917,
    "preview": "/*\n* The contents of this file are subject to the Common Public Attribution\n* License Version 1.0. (the \"License\"); you "
  },
  {
    "path": "r2/r2/lib/cache.py",
    "chars": 39355,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/cache_poisoning.py",
    "chars": 2174,
    "preview": "import hashlib\nimport hmac\nfrom pylons import app_globals as g\n\n# A map of cache policies to their respective cache head"
  },
  {
    "path": "r2/r2/lib/captcha.py",
    "chars": 2880,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/comment_tree.py",
    "chars": 14370,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/configparse.py",
    "chars": 4580,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/contrib/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "r2/r2/lib/contrib/activity.thrift",
    "chars": 2050,
    "preview": "include \"baseplate.thrift\"\n\n/** A unique identifier for a given \"context\".\n\nA context is an area of the service which a "
  },
  {
    "path": "r2/r2/lib/contrib/dtds/README",
    "chars": 103,
    "preview": "This directory provides a minimal DTD defining XHTML entities, for parsing\nHTML generated by markdown.\n"
  },
  {
    "path": "r2/r2/lib/contrib/dtds/allowed_entities.dtd",
    "chars": 18644,
    "preview": "\n<!--\n     Copyright 1998 - 2011 W3C,\n\n     Use and distribution of this code are permitted under the terms of\n     eith"
  },
  {
    "path": "r2/r2/lib/contrib/ipaddress.py",
    "chars": 72011,
    "preview": "#!/usr/bin/python3\n#\n# Copyright 2007 Google Inc.\n#  Licensed to PSF under a Contributor Agreement.\n#\n# Licensed under t"
  },
  {
    "path": "r2/r2/lib/contrib/rcssmin.py",
    "chars": 12506,
    "preview": "#!/usr/bin/env python\n# -*- coding: ascii -*-\n#\n# Copyright 2011, 2012\n# Andr\\xe9 Malo or his licensors, as applicable\n#"
  },
  {
    "path": "r2/r2/lib/contrib/simpleflake.py",
    "chars": 3359,
    "preview": "# ========================================================================\n# simple-flake - https://github.com/SawdustSo"
  },
  {
    "path": "r2/r2/lib/cookies.py",
    "chars": 5576,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/count.py",
    "chars": 1794,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/csrf.py",
    "chars": 2232,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/cssfilter.py",
    "chars": 17115,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/db/__init__.py",
    "chars": 1124,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/db/_sorts.pyx",
    "chars": 4572,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/db/alter_db.py",
    "chars": 2265,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/db/operators.py",
    "chars": 3811,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/db/queries.py",
    "chars": 63941,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/db/sorts.py",
    "chars": 1242,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/db/tdb_cassandra.py",
    "chars": 50622,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/db/tdb_lite.py",
    "chars": 2927,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/db/tdb_sql.py",
    "chars": 35457,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/db/thing.py",
    "chars": 51860,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/db/userrel.py",
    "chars": 7471,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/einhorn.py",
    "chars": 3766,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/emailer.py",
    "chars": 15364,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/embeds.py",
    "chars": 3182,
    "preview": "from datetime import datetime\nimport math\nfrom pylons import request\nfrom pylons import tmpl_context as c\nfrom pylons im"
  },
  {
    "path": "r2/r2/lib/emr_helpers.py",
    "chars": 6510,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/errors.py",
    "chars": 16805,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/eventcollector.py",
    "chars": 44906,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/export.py",
    "chars": 2294,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/filters.py",
    "chars": 10476,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/generate_strings.py",
    "chars": 2142,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/geoip.py",
    "chars": 5111,
    "preview": "#!/usr/bin/python\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \""
  },
  {
    "path": "r2/r2/lib/gzipper.py",
    "chars": 6870,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/hadoop_decompress.py",
    "chars": 4316,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/hardcachebackend.py",
    "chars": 12357,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/helpers.py",
    "chars": 1230,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/hooks.py",
    "chars": 4062,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/inventory.py",
    "chars": 12416,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/inventory_optimization.py",
    "chars": 16236,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/ip_events.py",
    "chars": 2182,
    "preview": "from collections import Counter\n\nfrom r2.lib.geoip import location_by_ips, organization_by_ips\nfrom r2.lib.utils import "
  },
  {
    "path": "r2/r2/lib/js.py",
    "chars": 23067,
    "preview": "#!/usr/bin/env python\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (t"
  },
  {
    "path": "r2/r2/lib/jsonresponse.py",
    "chars": 8625,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/jsontemplates.py",
    "chars": 55761,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/language.py",
    "chars": 8014,
    "preview": "import re\nfrom collections import namedtuple, Counter\n\nCharRange = namedtuple(\"CharRange\", \"name start end\")\n\nNONCHAR = "
  },
  {
    "path": "r2/r2/lib/lock.py",
    "chars": 5280,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/log.py",
    "chars": 8819,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/loid.py",
    "chars": 5606,
    "preview": "from datetime import datetime, timedelta\nfrom dateutil.parser import parse as date_parse\nimport pytz\nimport string\nfrom "
  },
  {
    "path": "r2/r2/lib/manager/__init__.py",
    "chars": 1124,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/manager/db_manager.py",
    "chars": 5345,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/manager/tp_manager.py",
    "chars": 3643,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/media.py",
    "chars": 32623,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/memoize.py",
    "chars": 2933,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/menus.py",
    "chars": 25454,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/merge.py",
    "chars": 3149,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/message_to_email.py",
    "chars": 9004,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/migrate/__init__.py",
    "chars": 1124,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/migrate/campaigns_to_things.py",
    "chars": 3687,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/migrate/migrate.py",
    "chars": 10633,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/migrate/mr_domains.py",
    "chars": 6592,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/migrate/mr_permacache.py",
    "chars": 11529,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/migrate/vote_details_ip_backfill.py",
    "chars": 2606,
    "preview": "import json\nfrom collections import defaultdict\nfrom datetime import datetime, timedelta\n\nfrom pylons import app_globals"
  },
  {
    "path": "r2/r2/lib/mr_tools/__init__.py",
    "chars": 1203,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/mr_tools/_mr_tools.pyx",
    "chars": 6011,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/mr_tools/mr_tools.py",
    "chars": 4915,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/mr_top.py",
    "chars": 5668,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/newsletter.py",
    "chars": 3856,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/normalized_hot.py",
    "chars": 3832,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/nymph.py",
    "chars": 7207,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/organic.py",
    "chars": 2739,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/pages/__init__.py",
    "chars": 1170,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/pages/admin_pages.py",
    "chars": 3784,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/pages/pages.py",
    "chars": 216237,
    "preview": "# -*- coding: utf-8 -*-\n# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. "
  },
  {
    "path": "r2/r2/lib/pages/things.py",
    "chars": 17376,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/pages/trafficpages.py",
    "chars": 30219,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/pages/wiki.py",
    "chars": 9151,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/permissions.py",
    "chars": 3688,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/plugin.py",
    "chars": 5327,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/profiler.py",
    "chars": 1552,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/promote.py",
    "chars": 44206,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/__init__.py",
    "chars": 2215,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/auth/__init__.py",
    "chars": 2253,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/auth/cookie.py",
    "chars": 2343,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/auth/http.py",
    "chars": 3402,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/cdn/__init__.py",
    "chars": 2489,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/cdn/cloudflare.py",
    "chars": 3416,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/cdn/fastly.py",
    "chars": 2222,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/cdn/null.py",
    "chars": 1392,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/email/__init__.py",
    "chars": 2188,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/email/mailgun.py",
    "chars": 3723,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/email/null.py",
    "chars": 1420,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/image_resizing/__init__.py",
    "chars": 2804,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/image_resizing/imgix.py",
    "chars": 4386,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/image_resizing/no_op.py",
    "chars": 1583,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/image_resizing/unsplashit.py",
    "chars": 1682,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/media/__init__.py",
    "chars": 2485,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/media/filesystem.py",
    "chars": 3076,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/media/s3.py",
    "chars": 5555,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/search/__init__.py",
    "chars": 1565,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/search/cloudsearch.py",
    "chars": 30526,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/search/common.py",
    "chars": 9975,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/search/solr.py",
    "chars": 30427,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/support/__init__.py",
    "chars": 2277,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/providers/support/zendesk.py",
    "chars": 6076,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  },
  {
    "path": "r2/r2/lib/ratelimit.py",
    "chars": 10951,
    "preview": "# The contents of this file are subject to the Common Public Attribution\n# License Version 1.0. (the \"License\"); you may"
  }
]

// ... and 705 more files (download for full content)

About this extraction

This page contains the full source code of the reddit-archive/reddit GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 905 files (7.6 MB), approximately 2.0M tokens, and a symbol index with 7491 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!